previous up next Adapter

Intent, Motivation, Applicability, Structure, Participants, Collaborations, Consequences, Implementation, Sample Code, Known Uses, Related Patterns

previous next Implementation

Although the implementation of Adapter is usually straightforward, here are some issues to keep in mind:

  1. Implementing class adapters in C++. In a C++ implementation of a class adapter, Adapter would inherit publicly from Target and privately from Adaptee. Thus Adapter would be a subtype of Target but not of Adaptee.
  2. Pluggable adapters. There are several ways to implement pluggable adapters. As an example, let's look at three ways to implement the TreeDisplay widget described earlier, which can lay out and display a hierarchical structure automatically.

    The first step, which is common to all three of the implementations discussed here, is to find a ``narrow'' interface for Adaptee, that is, the smallest subset of operations that lets us do the adaptation. A narrow interface consisting of only a couple of operations is easier to adapt than an interface with dozens of operations. For TreeDisplay, the adaptee is any hierarchical structure. A minimalist interface might include two operations, one that defines how to present a node in the tree, and another that retrieves the node's children.

    Given this narrow interface, here are three possible implementation approaches:

    1. Using abstract operations. Define corresponding abstract operations for the narrow Adaptee interface in the TreeDisplay class. Then it's up to subclasses to implement the abstract operations and adapt the concrete tree-structured object. For example, a DirectoryTreeDisplay subclass will implement these operations by accessing the directory structure:

      DirectoryTreeDisplay specializes the narrow interface so that its DirectoryBrowser client can use it to display directory structures.

    2. Using delegate objects. In this approach, TreeDisplay forwards requests for accessing the tree structure to a delegate object. TreeDisplay's clients can control the adaptation by supplying the delegate of their choice.

      For example, suppose there exists a DirectoryBrowser that uses a TreeDisplay as before. DirectoryBrowser might make a good delegate for adapting TreeDisplay to the hierarchical directory structure. In dynamically typed languages like Smalltalk or Objective C, this approach only requires an interface for registering the delegate with the adapter. Then TreeDisplay simply forwards the requests to the delegate. NEXTSTEP uses this approach heavily to reduce subclassing.

      Statically typed languages like C++ require an explicit interface definition for the delegate. We can specify such an interface by putting the narrow interface that TreeDisplay requires into a purely abstract TreeAccessorDelegate class. Then we can mix this interface into the delegate of our choice---DirectoryBrowser in this case---using inheritance. We use single inheritance if the DirectoryBrowser has no existing parent class, multiple inheritance if it does. Mixing classes together like this is easier than introducing a new TreeDisplay subclass and implementing its operations individually.

    3. Parameterized adapters. The usual way to support pluggable adapters in Smalltalk is to parameterize an adapter with one or more blocks. The block construct supports adaptation without subclassing. A block can adapt a request, and the adapter can store a block for each individual request. In our example, this means TreeDisplay stores one block for converting a node into a GraphicNode and another block for accessing a node's children.

      For example, to create TreeDisplay on a directory hierarchy, we write

      directoryDisplay := (TreeDisplay on: treeRoot) getChildrenBlock: [:node | node getSubdirectories] createGraphicNodeBlock: [:node | node createGraphicNode].
      If you're building interface adaptation into a class, this approach offers a convenient alternative to subclassing.


Implementation of Abstract Factory, Adapter, Bridge, Builder, Chain of Responsibility, Command, Composite, Decorator, Facade, Factory Method, Flyweight, Interpreter, Iterator, Mediator, Memento, Observer, Prototype, Proxy, Singleton, State, Strategy, Template Method, Visitor

previous next