Section 24.2. INDIRECTION AS A GENERALIZED PRINCIPLE: FROM POINTERS TO DESIGN PATTERNS AND BEYOND


24.2. INDIRECTION AS A GENERALIZED PRINCIPLE: FROM POINTERS TO DESIGN PATTERNS AND BEYOND

It has become a maxim of software design that "any problem can be solved by adding another level of indirection." At it simplest, indirection means the use of a pointer or reference instead of a pointed-to value directly, but over time indirection has come to carry more weight and have deeper meaning. It is instructive to trace the history of indirection, leading to the one problem that cannot be solved by adding indirectionthe problem of too much indirection. That problem requires a new kind of indirection.

24.2.1. More Than Pointers

Indirection in the form of addressing, pointers, or references is the foundation upon which all significant data structures are built. Since data structures are key to algorithms, indirection is also fundamental to a majority of significant algorithms. More significantly for our purposes, indirection is also fundamental to software engineering principles of modularity based on abstract data types.

However, dependencies are not just references or pointers; they are pathways of change propagation, and today references are so fundamental to software development that our mental model for indirection is wider than simply linking separately stored chunks of data. The semantics of indirection now includes a logical indirection in the behavior of the implementation of a separately defined service (polymorphism). The semantics is now widening again to include indirection in the behavior of the clients of a service (weaving or nonlinear composition).

24.2.2. Object-Oriented Indirection

Behavior indirection in object-oriented designs is a powerful dependency management technique. Figure 24-2 illustrates the fundamental idea of an object-oriented "hinge point" [12]the separation of implementation from interface in such a way that the implementation can be completely changed without affecting clients, as long as the contract of the interface is honored. Object-oriented indirection separates the "what" of a behavior from the "how" of that behavior. In an OO design, a direct dependency can be replaced by dependency upon a more stable and more abstract interface, freeing the implementation of that interface to change without brittle havoc. Interestingly, interface polymorphism has the effect of taking the original indirection of data structures to the extreme: hiding an object's data completely, leaving only a promise of its behavior.

Figure 24-2. An object-oriented "hinge point" removes an unwanted implementation dependency in favor of a more manageable pure interface dependency.


24.2.3. Design Patterns and Indirection

Object-oriented indirection is at the heart of many design patterns. Design patterns routinely employ indirection between service provider and service consumer (or service provider and augmented service provider). Each pattern adds extra apparatuses to the OO hinge point in a different way to solve a different problem. Table 24-1 lists many common design patterns, the purpose of the indirection found in them, and the name of the key interface that gives each its characteristic behavior. Most design patterns are, in fact, named after their key interface, an observation that lends weight to the claim that indirection is their central feature.

Table 24-1. Most Design Patterns Incorporate a Level of Indirection for Some Specific Design Purpose

Pattern

Indirection

Interface

Ref.

Observer

Decouples event receivers from event senders.

Observer

[7]

Decorator

Allows chained composition of multiple independent fragments of an overall algorithm.

Decorator

[7]

Adapter

Reshapes an existing interface to meet different client needs.

 

[7]

Extension Object

Dynamically provides multiple interfaces or extensions to a base service.

Extension

[6]

Iterator

Separates the traversal of a data structure from the data structure itself.

Iterator

[7]

Visitor

Allows unanticipated or non-core functionality to be added to a base object hierarchy.

ElementA

ElementB

ElementX

[7]

Type Object

Allows for dynamically typed object instances.

 

[9]

Strategy/State

Allows dynamic and potentially multivariate behavior.

Strategy

[7]

Memento

Externalizes the prior state of an object (e.g., for undo) without exposing its details.

 

[7]

Abstract Factory

Shields the client of an interface hierarchy from construction details and alternative families of implementations.

Abstract Factory

[7]

Property Container

Enables clients to read object attributes by name rather than via compiled interfaces.

Property Container

[2]


Czarnecki and Eisenecker [4] describe one key problem with design patterns: "object schizophrenia" or a "lost sense of self." A single domain class is augmented with mechanisms until it becomes a nearly unrecognizable jumble of implementation classes. This is a problem not only for design readability, but also for correct implementation and successful debugging. For example, levels of indirection in patterns like Decorator, Adapter, and Strategy mean that internal calls to the "current domain object" are no longer equivalent to simple calls to "this," "Me," or "self." At debug time, the Observer pattern can be a nightmare because multiple notifications to unknown clients (each hidden behind the same simple interface) can add surprising headaches when tracing through a particular notification or when debugging the interaction or ordering of multiple notifications. Czarnecki and Eisenecker discuss this problem in their chapter on aspect-oriented technologies. Aspect-oriented designs show great promise for overcoming the problem of too much indirection while avoiding improper dependencies.

24.2.4. Aspect-Oriented Indirection

Recall that object-oriented indirection separates "how" from "what," allowing many answers or an easily changed answer to the "how" for a single "what"one interface can have many implementations or one easily changed implementation. Aspect-oriented indirection separates "how" from "when" and allows a multi-valued or easily changed "when" for a single "how." In other words, a single implementation can apply at many locations in the execution of code even though it appears only once in the source.

The ability of an aspect-oriented compiler to insert behavior at multiple places in the execution of a program from a single place in the source code leads to the concept of an aspect-oriented hinge point (see Figure 24-3). An aspect-oriented design has the potential to break or avoid unwanted direct dependencies by moving the behavior that requires the dependency into a separate module (an aspect) that depends upon the otherwise directly connected client and service. Where an object-oriented hinge point introduces a new common dependency (the interface), an aspect-oriented hinge point introduces a common dependent (the aspect). With this new dependency management tool, many new design patterns wait to be discovered, and many old and even venerable design patterns may be ready for retirement.

Figure 24-3. An aspect-oriented hinge point, opposite to an object-oriented hinge point, introduces a common dependent rather than a common dependency.




Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net