May 93 - Choosing an Object-Oriented Development Toolkit
Choosing an Object-Oriented Development Toolkit
This article provides suggestions about what to look for when shopping for an object-oriented application development toolkit. The decision is important: you want to make a decision that will be proved right over a period of years. Anything less means a significant loss of investment in both time and money.
Watch Your Language
Choosing an OOP language may seem simple: C++. It does appear that C++ is garnering momentum right now, but rumblings of caution are already appearing from experienced quarters. Mikel Evins at Apple has written (in the Mar/Apr 1993 Frameworks) about experiences with object-oriented languages at Apple's Advanced Technology Group (ATG): "As ATG's ideas and ambitions become more sophisticated, the engineers gradually moved away from Object Pascal and C++. They found themselves... compensating for limitations in expressive power, and working around inappropriate mechanisms for abstraction".
Apple's solution was to develop a new object-oriented dynamic language (OODL), one that incorporated a dynamic object system. What Apple was looking for was "the right combination of OODL features, fast execution, and small object code size".
Object orientation is most often achieved by defining a new object-oriented language, or extending a procedural language to give it object-oriented features. This approach requires a new compiler, and often entails a whole new development environment. A few language extensions have become de facto industry standards as a result of blessing by an ANSI computer languages committee. Among these are C++ and CLOS. Just because it's an industry standard doesn't make it the best: MS-DOS is still the industry-standard operating system. More often, the result is a proprietary language and development environment product bundled together. (Apple's Dylan is a rather unique exception: a language with no product by its creator. Apple seems to have developed a penchant for promoting concepts without product: personal digital assistants (PDAs) are another example.)
An object system can be added to an existing procedural language without changing its syntax. This is done with best effect with languages that have great expressiveness and flexibility, such as Assembly and C (which is really a high-level assembly language). The advantage of an object system that isn't a language (or even a language extension) is that the object system works on any standard compiler for that language, giving a user a choice of development environments.
A dynamic object system allows methods and even inheritance to be defined at run-time. The drawbacks to a dynamic object system have historically been execution speed (necessitated by dynamic definition of inheritance and method attachment) and object code size (from using a memory image as the basis for running an application). Advances in object system design and implementation have resulted in dynamic object systems that can produce fast, tight code.
The right combination of dynamic object-oriented features includes the ability to redefine inheritance chains at run-time, and also define methods to classes, or even objects, at any time during program execution. A thin distinction between classes and objects is also a favorable ingredient. The flexibility of allowing objects to have their own methods and classes to have their own data provides freedom in software design and implementation. The alternative is to have to think of clever ways to do what dynamic definition lets you do in a straightforward way. In other words, if you don't have dynamic definition, you'll eventually be fighting the static language's limitations (although you may not realize it if you don't know any better). As developers gain more experience in working in object-oriented programming, static object systems are increasingly going to look like object-oriented dinosaurs.
A simple example of dynamic inheritance is to read in a dialog item resource for a file, creating dialog item objects, then adding the right class (control, picture, text, etc.) to a dialog item once the item type is discovered (by reading the resource data). This can be done in a static language by not assigning the dialog item type class before reading the dialog item resource definition, but that involves processing in a way dictated by the language's limitations, rather than doing things the way that might first come to mind (which is usually the easiest way) if no constraints were imposed. The flexibility of a dynamic object system brings both small and large benefits.
A major issue with languages is their longevity. You would like to pick a language that is going to be around. Proprietary languages, or languages that are offered only by a single vendor, carry a substantial risk. Large companies abandon products if they don't sell well, or even if they just no longer meet the company's strategic focus. Small companies disappear. There are already some prominent examples of this in the Mac development tools market. There are, no doubt, more to come. If you are considering a proprietary language or environment, consider well the commitment of a vendor to its product (or the company's own long term viability, if you are buying from a small company).
So, besides picking a powerful, flexible object system, you should consider the vendor. Although few vendors offer it, full source code is the only safe bet. That way, you have control over your own development destiny, regardless of its vendor's fortunes or commitment.
Ask vendors you are considering how much source code they offer. If source code is not available, ask whether source code is in escrow ("in the can"), so that customers have access to the source if the company drops the product or goes out of business. That's the worst-case scenario, but if a vendor hasn't considered code in escrow, you know it hasn't really made customer satisfaction a high priority.
The Right Environment
Dynamic development environments may be the wave of the future. With their incremental linkers, THINK C and Pascal have satisfied the Macintosh programming masses for years with their quick turnaround. New dynamic development environments take quick turnaround to a new level: allowing code to be modified and tested without having to exit the application being tested. New code is inserted like brain surgery on-the-fly.
Don't be fooled about the distinction between a dynamic environment and a dynamic object system. While a dynamic environment may look slick, it's no substitute for a dynamic object system. Dynamic environments only boost productivity in the code testing cycle; they do not make a static language dynamic. If you feel you have to make a choice between a dynamic object system and a dynamic environment, an dynamic object system is a better long-term bet. A dynamic object system can let you implement complex code in the most straightforward manner, helping to minimize the debugging phase, which is the only thing a dynamic environment helps you with.
As with choosing a language, consider carefully the risk involved with being tied to a proprietary development environment. Code that has to be extruded to another compiler to build a stand-alone application should be a red flag that the development environment is incomplete. How does one fix a problem that shows up only in the stand-alone application version, but not in the development version?
A top-flight class library should provide a full set of services in the following areas: user interface, event handling, data management (the closer to an OODB, the better), and document management. A class library that concentrates on user interface hasn't provided the full set of services you'll need to develop an application.
Judging the quality of a class library before using it is difficult. One rule of thumb is the more features in the fewest number of classes, the better. A class library with hundreds of classes may be an indicator of functional splintering caused by design by committee, or simply too many programmers whacking away without proper coordination. Or it may just be the result of poor design at work.
Another rule of thumb is whether implementation has matched conceptual simplicity. Take, for example, the way events are handled. The simple way to handle a mouse selection, for example, is for the window owner/actor to look at the objects in its window's view(s) to see which is selected. (Visually, an actor has a window; the window has one or more views; each view has objects in the view.) A class library that handles events radically different from the visual hierarchy is a sure sign of poor architecture. A class of bureaucrats, and event handling that requires a supervisor, clearly indicates that the library designer(s) didn't take the simplest route. Complex code is like complex machinery: more moving parts means it's harder to get right, and easier to break.
Memory management is one area where a toolkit can be clearly judged. Macintosh and Windows memory managers were not designed for allocating large numbers of small blocks, which is the case with object orientation. The only solution is a pointer-based memory manager designed for tens of thousands of allocations. Any object-oriented class library that does not have its own memory manager puts you at a terrible speed disadvantage.
Applications built with object orientation don't have to be fat and slow. There are object-oriented application frameworks that produce fast, lean applications. Ask vendors for a demo that has a sample application. Check how fast the demo is, and how big the executable.
Besides sprouting like mushrooms, application frameworks are becoming increasingly sophisticated. All too often, sophistication implies complexity. Complexity implies hard to learn and use.
As a result of their complexity, early class libraries have given object orientation a bad reputation as being difficult to use. (Why use a code library that's as hard to learn as the Macintosh Toolbox?) Without some way to manage complexity in the application programming interface (API), that reputation is going to grow.
Polymorphic functions to the rescue. By using the same functions for similar functionality, a class library's API can be minimized. Any application framework should take advantage of function polymorphism to simplify the API. A class library with an enforced API has a great start in the battle of managing complexity.
The ideal, enforced way to minimize an API is through generic functions, where a method can be accessed only through a polymorphic function. (Generic functions are called verb functions by one vendor.) With generic (or verb) functions, each function has methods associated with it, in parallel to the idea that a class has methods associated with it. Object-oriented programming can quickly lose its appeal with an alphabet soup of classes and hundreds or thousands of method names to memorize or constantly reference.
There is another important issue related to application framework complexity. Using objects of any single class can be managed easily. But as objects of different classes interact, understanding or having to deal with these sometimes complex interactions can result in the same kinds of maintenance headaches that occur with procedural programming.
Some superstructure that encapsulates behavior on a large scale should be available in an application framework. This superstructure should allow creation, running, and modification of a subsystem of processing that involves interaction among objects of several classes.
Event processing is a good case in point. A rather ridiculous way to handle event processing is to have an event subclass for every event type, where each subclass has one method to handle one event type. Applying classes to subsystem processing in this way results in inappropriate abstraction. A more intelligent way to handle event processing is to have some enumerated event processing agent, where event handling for an event type can be modified by plugging in a function call or method override. It seems more natural to think of an event agent with plug-ins, rather than a glob of event-specific classes.
One of the major maintenance problems with procedural programming is that poor modularity causes side effects in making changes in code. Object orientation solves that to such a degree that the inverse problem arises.
By definition, classes have a high degree of modularity: so much so that a major design issue is how best to set up and maintain links between objects. Objects are often linked to other objects. A view keeps track of the objects in the view, for example.
Though few products have it, a well-implemented development toolkit should have a simple, robust mechanism for maintaining object links. Garbage collection, another essential feature of a good class library, should be simply part of object link maintenance.
A cross-platform class library by necessity must lightly use operating system services. A well-designed class library is much like an elaborated operating system, but with a difference. The Macintosh in particular lacks a high degree of modularity. In the Mac Toolbox, text, controls, and lists are dependent upon a particular window. The Mac OS was not designed for flexible object orientation. The more a class library looks like a translation of the Macintosh Toolbox into an object-oriented format, the less flexible and portable it is.
Controls are a case in point. Macintosh controls must appear in a window that has its top-left coordinate at (0,0). If you try to draw a checkbox or radio button into an offscreen port, or not at (0,0), you get a push button. Don't even try putting Macintosh controls into a scrolled view. Windows controls are child windows. There might be a good reason why a control has to be a window, or tightly bound to one, but it clearly isn't modular software design. The only solution for a decent class library is to implement controls from scratch.
At least one well-known class library is "view-happy", requiring that everything in a view is a subview. A view is a presenter of a collection of objects. So, conceptually, it makes no sense that the picture or icon class (objects of which appear in views) should have to be view subclass. Just because something is object-oriented doesn't mean it is thoughtfully modular. While classes by definition are modular, forced relations between objects of different classes can weaken the modularity of a class library as a whole.
An important question to ask vendors is whether files generated using toolkit code can be read on any supported platform. Platform-independent file generation is an important issue if you are going to be offering multiple platform versions of your product.
A solid cross-platform class library has to be designed for platform independence. It's not something that just happens in implementation. Most frameworks vendors proclaim a readiness to deliver a cross-platform version in the coming months. Approach such claims of vaporware with justified skepticism. Ask vendors how many Toolbox Managers have been used in the core class library. If you hear much more than "menus, windows, and drawing primitives", you know that the product isn't going to be cross-platform in the near future (regardless what the vendor claims).
Support for the Future
Technical support and upgrade track record are two important considerations when choosing a vendor. The ideal is to find a vendor that offers unlimited technical support, and has frequent upgrades to its toolkit.
Unlimited technical support is an expression of confidence on the vendor's part that the code is solid and the documentation is good. Frequent upgrades indicate that the vendor is constantly at work on improvements, and helps prove that the toolkit is amenable to rapid progress. Solid code and good documentation minimize the need for technical support, and make it easy for a vendor to give technical support when asked. A company that charges hundreds of dollars for technical support is a red flag that either: a) the company itself believes the toolkit is hard to learn and use, b) the documentation is inadequate, necessitating a heavy load of technical support, or c) the company is run by bean-counters or vulture capitalists interested in milking the cash cow, and you are the cow!
There are a lot of promises, and some promise, in the OOP toolkit market. There is already a fine array of application development toolkits available, and more coming. Not all will be around in a few years; the market provides the winnowing process.
Choosing a development toolkit is like getting married: it leads to either deepening appreciation or divorce. And divorce can waste months and years of the your life. Choosing a good toolkit can save time and frustration, accelerate application development, and may even widen your software development horizons.