Software Development
September 2002

Back to the Universe

Beyond object-oriented composition, connection-oriented composition and contextual composition, who's ready to implement proximity-based or data-driven composition? Part 2 of 2.

By Clemens Szyperski

As we discussed in last month's column, "The Universe of Composition" (Beyond Objects, Aug. 2002), there are other pattern possibilities available for composition aside from the three dominant composition patterns in use today (object-oriented, connection-oriented and container-based). For example, composition can be proximity-based: If two objects are "near" each other—such as inside the same context—they may be automatically connected.

Making Connections
All these composition approaches have analogs in the physical world. Object-oriented composition is like connecting to an Internet hub, where the entire network's wiring and switching is established behind the scenes. Connection-oriented composition is like taking separate electronic components and wiring them to each other. Contextual composition is like using a cellular phone that automatically interacts with a base station. Proximity composition is like two laptops forming a wireless peer-to-peer network when placed a short distance from each other.

A further evolution of the composition universe leads to the notion of data-driven composition or, in general, dynamic context-sensitive composition. For example, an application may compose its user interface dynamically to accommodate the nature of the data being used, as in a message-oriented system that activates components based on the type of incoming messages. In such a system, composition is the result of the actual data flow.

Which Way?
The spectrum of compositional approaches is quite rich, and the selection of the right approach for a given problem is subtle. One important criterion to guide our selection is the stability of a composition under change of its parts (see sidebar, "Strong Bonding").

To make a composition stable, we require that:

  • The boundaries are explicitly specified. (We need to capture the entire "hull" around an object.)
  • In the case of data-driven composition, a designated object holds the additional knowledge that the composition is correct. (This object is typically a separate meta-object.)

Composition Properties
What is a correct composition? To answer this question, it's useful to think of the result of a composition as a composite instance, or just composite for short. Technically, the actual properties (functional and extra-functional) of such a composite should meet the specification of what the composition was supposed to achieve.

Compositional reasoning allows inferring that a composition is correct by merely operating over the composition description and the specifications (not implementations) of the part components. The composition declaration can range from a simple list of "connections" or "wires" to additional assertions that do not follow from the component's specification, but do reflect additional knowledge available to the composer. (Such a composition declaration can itself be packaged as a component. Instantiating this component yields a composite instance.)

Static checking schemes, such as type systems, can be used to eliminate certain classes of composition errors. Checking techniques are almost always conservative: They usually work at the price of also disallowing some compositions that would be correct, but cannot be statically determined as such. Composition operators are sound if any composition that meets the static checking scheme's rules yields actual composites that behave within the bounds predicted by the static checking scheme.

Strengths and Weaknesses
All of the composition techniques (explicit, container, data-driven and so on) have strengths—and weaknesses—in terms of potential guarantees and flexibility. Thus, moving forward, advanced systems will have to draw on all these techniques in carefully chosen combination. Various examples in the complex enterprise applications space include the combination of workflow engines, message queuing systems, attribute-driven containers and explicitly composed fine-grained components. Qualities of the different composition approaches can be analyzed relative to specific properties. For instance, we may ask how stable a composition approach is if the involved components are allowed to version independently (see "Strong Bonding"). As another example, we can list characteristic scenarios that ask for a particular preferred composition style (see sidebar, "Pick and Choose").

Our overall understanding of the theoretic and practical limitations and trade-offs among these various composition approaches is far from complete. Much future work, both in research and in advanced development, is needed to close these gaps—upon which component nirvana will immediately ensue.

Strong Bonding
A composition pattern's ability to withstand change should help you determine which one is best for you.

Some compositions are meant to last longer than others. This is particularly challenging if the composed parts are allowed to version separately. Here is a partial list of different levels of composition stability:

Asymmetric composition
Objects with implicit dependencies on other classes:

  • May be composed (used together) if the dependencies permit it and if the dependencies meet requirements of composing party.
  • Are highly fragile under versioning: Changes to inherited classes can change the way classes interact "under the hood," likely breaking some compositions.

Container-provided services:

  • Can compose objects by selecting appropriate container based on attributes.
  • Can be stable under versioning since container abstraction provides strong isolation boundary (no random inheritance, controlled and specified protocols between container and containees).

Symmetric composition
Objects with explicitly required interfaces:

  • Must have explicit external "wiring" to make required interfaces meet compatible provided interfaces.
  • Can be stable under versioning (no random inheritance, specified protocols at the level of required/provided interface connections, and interfaces can support cross-version negotiation).


Pick and Choose
Explicit, contextual, data-driven: How do you select the pattern that's best for your project?

You should choose the composition style that makes the most sense. Here is a partial list of composition-style possibilities and how they're used:

Explicit composition is the static "wiring" of objects (plus possible asymmetric co-dependencies):

  • The third party is the developer declaring the desired composition.
  • Examples include ADLs (architecture description languages), BML (IBM Research's bean markup language) and visual bean assembly tools (like Sun's BeanBox).

Contextual composition is the placement of objects in appropriate containers based on attributes:

  • The third party is the developer of the container that integrates a palette of services (note the problems faced when allowing for extensible containers; all mainstream containers are de facto closed for this reason; however, note that Microsoft's .NET CLR context mechanism is not actually closed).
  • Examples include COM apartments, MTS contexts, EJB containers, COM+ contexts, CLR AppDomains and contexts.

Data-driven composition: Objects are wired to other objects and/or placed in appropriate containers based on the runtime observation of the data's characteristics:

  • The third party is the developer of the composition engine (note the problems of extensibility detailed above; they are aggravated because of increased flexibility when using more than data-driven contextual composition, since point-to-point composition is less containable in its effects).
  • Examples include workflow engines.

Objects communicate, in data-driven composition, through messaging spaces like a bus or tuple space.

  • Examples include message queuing systems.