6. Delegation

Unlike English, Spanish has two verbs that mean "to be." Ser is used to express relatively permanent states of being such as nationality and gender, while Estar is used to express impermanent states of being such as health and wealth.

Programmers can use specialization to express some states of being. For example

A secretary is an employee.

could be expressed in C++ as:

class Secretary: public Employee { ... };

Unfortunately, Java suffers from the same lack of expressiveness as English when it comes to distinguishing permanent from impermanent states of being. For example, expressing:

A handyman is a carpenter.
A housekeeper is a cook.

using specialization:

class Handyman extends Carpenter { ... }
class Houskeeper extends Cook { ... }

causes trouble when the same handyman gets a job as a plumber or electrician, or the housekeeper is performing cleaning or laundry services. A Java handyman object is a block of carpenter attributes fused to a block of handyman attributes:

We cannot dynamically (i.e., while the program is running) change the base class of a handyman:

class Handyman extends Plumber { ... }

Multiple inheritance is allowed in C++ (but not Java):

class Handyman:    public Carpenter,
                     public Plumber,
                     public Electrician { ... };

may be inefficient because every handyman object now carries the attributes of all three base classes:

.

But some handymen may never get jobs as plumbers or electricians, while others may find work at jobs we haven't anticipated, such as gardening or painting.

Objects as States

We can express impermanence in C++ using the State Design Pattern, which decouples an object from its possible states:

State [Go4]

Other Names

Objects as States

Problem

The attributes and behavior of an object, x, may depend on its state, but its state may change dynamically. This can lead to member function implementations based on awkward multi-way conditionals that are difficult to maintain:

   switch (state) {
      case state1: ...
      case state2: ...
      // etc.
   }

Solution

Implement each branch of the multi-way conditional as a member function in a separate class derived from an abstract state base class. The member functions of x simply forward client requests to the corresponding member functions of the associated state object, which may be changed dynamically.

Static Structure

Assume an object of type Context provides three services: A, B, and C, but that the nature of these services depend on the state of the object, which may change while the program is running. We add a reference to the Context class that points to an instance of a new State class, which also provides services A, B, and C. However, these services are not implemented in State, making State an abstract base class. Instead, these services are implemented in classes derived from the State class:

Here's how the State class might be declared:

abstract class State {
   abstract void serviceA(Context c);
   abstract void serviceB(Context c);
   abstract void serviceC(Context c);
}

State1, State2, and State3 are simply concrete classes derived from State:

class State1 extends State { ... }
class State2 extends State { ... }
class State3 extends State { ... }

The Context class maintains a reference called state that points to an instance of a class derived from State. We don't need to be more specific about this other than to say that state is of type State. Each Context member function simply calls the corresponding member function of the current state, forwarding its implicit parameter as an argument:

class Context {
   void serviceA() { if (state != null) state.serviceA(this); }
   void serviceB() { if (state != null) state.serviceB(this); }
   void serviceC() { if (state != null) state.serviceC(this); }
   void setState(State s) { state = s; }
   // etc.
   private State state = null; // current state
   // etc.
}

Clients or any of the state member functions might change the state of the context using the Context.setState() function. This will instantly change the behavior of the context's services. For example:

class State1 extends State {
   void serviceC(Context c) {
      if (condition2)
         c.setState(new State2());
      else if (condition3)
         c.setState(new State3());
      // etc.
   }
   // etc.
}

Returning to our handyman example, we might think of a handyman's job as his state:

class Handyman {
   void getToWork() { if (job != null) job.getToWork(this); }
   void setJob(Job j) { job = j; }
   private Job job = null;
}

Where Job is an abstract base class for various specific types of jobs, and Handyman is the context:

abstract class Job {
   abstract void getToWork(Handyman h);
}

Electrician, plumber, and carpenter are concrete handyman jobs:

class Electrician extends Job {
   void getToWork(Handyman h) { /* change light bulbs, etc. */ }
}

class Plumber extends Job {
   void getToWork(Handyman h) { /* plunge toilets, etc. */ }
}

class Carpenter extends Job {
   void getToWork(Handyman h) { /* mend fences, etc. */ }
}

The state pattern should only be used when the nature of a state is too complex to be characterized by a single variable containing a simple value such as an int, string, or float. Also, frequent state construction and destruction can be inefficient if they are complex.

Delegation Defined

The state pattern is one of many design patterns that use delegation. According to the dictionary, delegation means:

To entrust a power or responsibility to an agent.

In the case of the state pattern, the context object is entrusting the power and responsibility of implementing its services to its agent or delegate, the associated state object. More concretely, delegation means that an object transparently forwards client requests to a hidden delegate object, which may be dynamically changed:

class Context {
   void serviceA() { if (state != null) state.serviceA(this); }
   // etc.
}

Like specialization, delegation is a reuse mechanism. For example, if x is an instance of the Handyman class, and if x.job points to an instance of the Electrician class, then it appears to the clients of x that the Handyman class extends the Electrician class.

But as a reuse mechanism delegation differs from specialization in two important ways. First, if y is also an instance of the Handyman class, but y.job points to an instance of the Plumber class, then y's client believes that x's client is crazy. Handyman doesn't extend the Electrician class, clearly it extends the Plumber class! In other words, delegation is an inheritance mechanism at the level of individual objects, not of classes.

Secondly, if

x.setJob(new Carpenter());

is called, then x's client might believe that he has indeed gone crazy. A minute ago it appeared as though the Handyman class extended the Electrician class, but now it appears that it extends the Carpenter class. In other words, delegation is a dynamic inheritance mechanism. Languages like Smalltalk and LISP use delegation as the default inheritance mechanism.

Delegation Styles

There are several styles of delegation. The parameterized style used in the earlier examples assumes a reference to the context will be passed to the state's member functions:

abstract class State {
   abstract void serviceA(Context c);
   abstract void serviceB(Context c);
   abstract void serviceC(Context c);
}

Alternatively, State can be an interface:

interface State {
   void serviceA(Context c);
   void serviceB(Context c);
   void serviceC(Context c);
}

The parameterless style assumes the state base class maintains a reference to its context:

abstract class State {
   State(Context c) { context = c; }
   abstract void serviceA();
   abstract void serviceB();
   abstract void serviceC();
   protected Context context;
};

By declaring the context reference to be protected, it becomes visible to the implementations of the derived class member functions. Of course the derived class constructors must remember to call the base class constructor:

class State1 extends State {
   State1(Context c) { super(c); ... }
   void serviceA() { ... }
   void serviceB() { ... }
   void serviceC() { ... }
}

Adapters

Have you ever fried a hair dryer or electric shaver while travelling in a foreign country? The problem is that in some countries a walloping 220 volts comes out of the outlets, while most appliances made in the US expect a meager 110 volts. There are three solutions to the problem: grow a beard and sun dry your hair, use battery powered appliances, or put an adapter between the foreign outlet and your hair dryer. An adapter is a device that turns a 220 volt input into a 110 volt output.

Programmers can also face this problem. Assume a client expects a server object to conform to a specific interface. As implementers of the server, our first duty is to shop around to see if there are any server classes that can be reused. Assume we find such a class, but its interface doesn't quite conform to the interface expected by the client. This is analogous to the problem with the foreign electrical outlet that provides electricity, but doesn't conform to the interface expected by the electric razor. Our solution is analogous, too:

Adapter [Go4]

Other Names

Wrapper

Problem

An existing server class implements the functions required by a client, but the interface doesn't match the required interface.

Solution

Insert an adapter between the client and the server. The adapter implements the interface required by the client by calling the corresponding server functions. The adapter and server can be related by specialization or delegation.

Static Structures

The adapter design pattern defines a collaboration between four classes: the client, the server interface, the adapter, and the adaptee (i.e., the server with the wrong interface). The adapter implements the server interface in the sense described above. The client holds a reference to an adapter, but typed as a reference to a server. There are two possible relationships between the adapter: specialization or delegation.

If the adapter specializes the adaptee, then it can call the adaptee functions directly:

Alternatively, the adapter can access the adaptee services by delegation:

Implementations

Both structures assume the interface expected by the client is specified by an abstract server base class or interface:

interface Server {
   void serviceA();
   void serviceB();
   // etc.
}

and the misnamed implementations are provided in the adaptee class:

class Adaptee {
   void serviceX() { ... }
   void serviceY() { ... }
   // etc.
}

The client invokes the server's services through a reference:

class Client {
   void doSomething() {
      myServer.serviceA();
      myServer.serviceB();
      // etc.
   }
   // etc.
   private Server myServer;
}

In the specialization solution the adapter implements the server interface by calling the privately inherited adaptee functions:

class Adapter extends Adaptee implements Server {
   void serviceA() { serviceX(); }
   void serviceB() { serviceY(); }
   // etc.
}

In the delegation implementation the adapter implements the interface by forwarding messages to the adaptee:

class Adapter extends implements Server {
   void serviceA() { adaptee.serviceX(); }
   void serviceB() { adaptee.serviceY(); }
   // etc.
   private Server adaptee;
}

 

Handle-Body Idioms

Generally speaking, a handle-body pair is a pair of objects collaborating to appear to the client as a single object. One object, called the handle, manages the interface with the client, while another object, called the body, provides the application logic.

Here is a general outline of a handle-body declaration:

class Body { // no client access, handles only!
   void serviceA();
   void serviceB();
   // etc.
}

class Handle {
   void serviceA() { if (myBody != null) myBody.serviceA(); }
   void serviceB() { if (myBody != null) myBody.serviceB(); }
   // etc.
   private Body* myBody;
}

Note that by default, all Body members are private. Handle objects can access the body members because of the friend declaration.

Applications

Adapter-Adaptee and Context-State are two examples of Handle-Body pairs. There are many other examples.

The Body is a Shared Object

The Body might be a shared object, such as a large CAD-CAM model or a representation of a unique resource such as an Active X control, a file, or hardware device. Although there is only a single body, there may be multiple handles, one for each client, creating the illusion that each client has its own copy of the shared object.

The Body is a Remote Object.

In distributed client-server applications, client and server objects reside in different processes possibly running on different machines connected by a network. We want to shield client objects from the details of remote communication (sockets, IP addresses, pipes, ports, protocols, etc.) and instead allow clients to communicate with server objects (the body) using ordinary method invocations. We can achieve this by introducing a client-side proxy (the handle) that encapsulates the low-level communication details.

Division of Labor

Sometimes the responsibilities of an object naturally divide into two categories. In these situations it is often a good idea to separate these responsibilities into two associated objects. In the orthodox canonical form, for example, the handle performs memory management duties while the body implements application logic. In the Model-View-Controller design pattern (see Chapter ?) a view object (the handle) is responsible for user input and output, while a model object (the body) implements application data and logic.

The Body is not a Java Object.

How do we integrate objects from other languages into a Java program? Naturally, clients want to be shielded from the syntax and semantics of the foreign object. The idea is to encapsulate a reference to the foreign object (the body) in a Java object called a wrapper (the handle), which also encapsulates the syntax and semantics of the body. Clients only see and use the wrapper. This technique the technique used in the MFC application framework. Many MFC objects are C++ objects that encapsulate references to C objects called windows that are managed by the operating system.

The Body is a Data Structure

Object oriented programming doesn't simplify the task of implementing data structures such as linked lists or 2-3 trees. As in non object-oriented implementations, such structures are composed of many "cells" that are delicately linked together by references. Clients must exercise care when inserting or removing items from these structures. Often complicated re-balancing or re-linking operations are required. In many cases new cells must be allocated while old cells must be deleted.

However, object oriented programming can provide objects that encapsulate and manage these data structures. In this case we can think of the data structure as a body and the manager as a handle. A manager provides clients with member functions for inserting, removing, and retrieving items stored in the data structure, which is otherwise inaccessible to them. The complicated administrative duties that accompany inserting and removing items are automatically performed by the manager. Eventually, clients come to identify the manager with the data structure in manages. Collection framework containers such as lists and sets are really managers of linked lists and dynamic arrays, respectively.

Shared Bodies

Decorators

Specialization allows us to add features to a class, but in some situations we need to add different combinations of features to individual objects. For example, a grid encapsulates and manages a dynamic two dimensional array of characters that provides "poor man's" graphics:

Suppose we will also need a shaded grid (i.e., a grid with a shaded background), a bordered grid (i.e., a grid with a border around it), a read-only grid (i.e., a grid with disabled plot functions), a shaded-read-only grid, a bordered-read-only grid, a shaded-bordered grid, and a shaded-bordered-read-only grid. If we create a class for each combination of these features we will end up with a complicated hierarchy of seven classes:

Adding a new feature, such as resizable grid, will add six new derived classes. In general, if we are contemplating n features, there will be n! (= n * (n - 1) * ... * 1) combinations of these features. This can lead to a combinatorial explosion in the number of classes we must implement and maintain.

We can avoid this maintenance nightmare by using delegation instead of specialization to add features. For each feature we simply create a handle object called a decorator that implements the feature, then delegates to its body. The body may be the original component or another decorator. Thus, decorators can be chained together to dynamically provide any combination of features:

The Decorator Pattern formalizes this idea:

Decorator [Go4]

Other Names

Wrapper

Problem

In some situations we may want to add features to an individual object rather than an entire class.

Solution

For each additional feature create a new "decorator" class that implements the feature. The decorator class also implements the interface of the original object by delegating to the original object or perhaps to an instance of another decorator class. In this way decorators (i.e. instances of decorator classes) can be chained together.

Static Structure

Assume we want to add features to an instance of the Component class that implements a component interface. Each decorator class must also implement the component interface. In this way the client never knows if it is dealing with a component or a decorator. Each decorator must be able to delegate to its body, which may be the original component or another decorator. (Thus, decorators are component interface clients, too!) For convenience, we place the delegation machinery in a decorator base class:

For example, the services are virtual functions in the component interface:

interface IComponent {
   void serviceA();
   void serviceB();
   // etc.
}

The basic services are implemented in the Component class:

class Component implements IComponent {
   void serviceA() { ... }
   void serviceB() { ... }
   // etc.
}

The Decorator base class simply delegates to its body:

class Decorator implements IComponent {
   Decorator(IComponent c) { myBody = c; }
   void serviceA() { myBody.serviceA(); }
   void serviceB() { myBody.serviceB(); }
   // etc.
   private IComponent myBody;
}

Suppose the concrete decorator class Decorator1 simply enhances serviceA() with some added behavior, then it only needs to re-implement serviceA() (the Decorator class provides default implementations for the other services). The last line performs the delegation:

class Decorator1 extends Decorator {
   Decorator1(IComponent c) { super(c); }
   void serviceA() {
      // added behavior goes here
      super.serviceA(); // delegates to body
   }
}

Here is how a client might create a reference, z, to an object that implements the basic component interface, but with the features added by Decorator1 and Decorator2:

IComponent x = new Component();
IComponent y = new Decorator1(x);
IComponent z = new Decorator2(y);

Of course the original component and the Decorator1 instance can be anonymous, so we can create z in a single line:

IComponent z = new Decorator2(new Decorator1(new Component()));

Here is a sequence diagram showing the chain of delegations that occur when the client calls z.serviceA():

Be careful, as with the Envelope-Letter idiom, a decorator derives from the component interface and delegates to an instance of the component interface. We need decorators to be derived from the component interface so they can be treated like components by clients and other decorators. However, a decorator must be careful never to use the functions and attributes it inherits from the component interface base class. Instead, only the functions and attributes of the body must be used. For example, assume state is a component interface attribute.

abstract class IComponent {
   protected State state;
   // etc.
}

The true state of z is x.state, not the inherited z.state.

Streams

The decorator pattern is used to add features to java streams. Recall the makeWriter() method of the Tools class:

static public PrintWriter makeWriter(OutputStream os) {
   return new PrintWriter(
      new BufferedWriter(
         new OutputStreamWriter(os)), true);
}

This method constructs an output stream writer, places a buffered writer decorator in front of it, then places a print writer decorator in front of that. Each extends the abstract Writer interface/class:

A call to this method constructs the decorator chain:

Problems