|
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.
| |