Software Development Universe of CompositionComponents can mesh via objects (plugging into a hub), connections (wiring electronic boards together) or contexts (a cell phone calling a base station). Which to use when? Part 1 of 2.By Clemens SzyperskiComponents are units of composition. This claim seems redundant—isn't that what they do by the very name they're given? Indeed. However, exploiting the full potential of components requires a thorough understanding of composition. This column investigates the surprisingly wide spectrum of composition techniques explored thus far. In all likelihood, more will be discovered as our field evolves: We're nowhere near a universal understanding of the techniques of composition. Perhaps surprisingly, although they can be combined, there is no single technology that supports all the composition techniques discussed here. (Actually, the CORBA Component Model gets close to combining them all, but it remains to be seen whether it will leave its mark in actual applications.) Perhaps this is due to a lack of understanding of this field's richness. I hope that this short article will inspire many to go forth and explore composition. Objects, Containers and Connections Before we begin with object-oriented composition, let's take a step
backward. How do we compose functions (real ones—not the unrelated items
of the same name found in C and elsewhere)? Simply by nesting. The result
of one function is used as an argument for another: Side Effects Objects Do Not Compose Real objects tend not to be that simple. Their implementing classes draw on other classes, either by instantiating them or by taking references to their instances. However, if we focus our specifications only on the side of incoming method calls, we can't actually talk about what objects do to other objects, since that's done with outgoing calls (calls that originate in the object of interest and are directed at other objects). If we captured all such outgoing dependencies in our specifications, we'd be in a similar position as with functions that have side effects. The picture may not be pretty, but composition would be possible. Now, have a look and ask yourself how many specifications for typical class libraries you know that actually provide that level of specification. Close to none is probably the standard here. Even a class specified using the full design-by-contract approach—with pre- and postconditions as well as a class invariant—fails to capture outgoing calls. The only outgoing calls that are frequently documented in some form are events that listeners can register for. Object Models Do Compose ... Sort Of Even if all this information is available and we therefore find confidence when composing objects, another problem looms. When using an object that is an instance of a huge class library, we're likely to be creating a dependency on a large number of classes. After all, every object we use is likely to use other objects, which again use further objects and so on. Ultimately, a dependency on a good part of the entire system can result. Such dramatic transitive dependencies make it difficult to argue convincingly that composition will work. They also make it difficult to gain confidence that all reasonable composition products can actually be realized. More likely than not, there will be large classes of cases in which a particular class or object would be just fine to do the job, but not if that meant pulling in all its transitive dependencies that don't do what is required. From Objects to Components Explicit dependencies are but the first step. One way to gain compositional flexibility is to move from explicit to parametric dependencies. For example, instead of making a class depend on another class, make it depend on an interface and equip it with a mechanism to connect to any class that implements that interface. Now this class can be used in many different compositions. Class libraries that support the reading and writing of streams are typically factored this way: A variety of reader and writer objects specialize in data formatting and can be connected to a variety of stream objects that specialize in the actual stream implementation. Another example is the event/listener pattern found in many component class models such as JavaBeans or .NET Windows Forms, or even at the language level in C#. Classes that are designed to depend only on interfaces and therefore have fully parametric dependencies are a rare exception. However, it is these classes that form the most useful foundation for components, since the same class can now be used in separate compositions without any fear of cross-composition side effects. Security is another interesting aspect: If a class can use only what it has been connected to, overall behavior is much easier to contain. If taken to the limit, where all class interaction is based on connections and where composites can abstract compositions hierarchically, we arrive at connection-oriented composition. Contextual Composition How is a container different from a platform? It depends. Some platforms actually have container properties. For example, operating systems that strongly isolate processes form process containers. As a result, two applications running in separate processes of such an operating system can communicate with each other only through the mechanisms provided and controlled by the operating system. Other platforms don't form containers: They don't intercept all incoming and all outgoing communications and thus can be bypassed. How is a container different from a set of connections? A container completely encloses its contained instances, while the same can be established only by convention for peer-to-peer connections. Also, a container is implicitly connected to all instances inside—all enclosed instances uniformly benefit from their single shared container instance's services and all are uniformly constrained by that container's policies. Composition to No End Next month, we'll venture beyond current common practice by looking at some taxonomies that help us to better understand the trade-offs involved when using the different composition techniques. We'll also take a peek into the probable future by examining some further possible composition techniques. |