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.
Adapter-Adaptee and Context-State are two examples of Handle-Body pairs. There are many other examples.
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.
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.[1]
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.
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.
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.
According to the dictionary, delegation means:
To entrust a power or responsibility to an agent.
In the case of the Handle-Body idiom, the handle object is entrusting the power and responsibility of implementing its services to its agent or delegate, the associated body object. More concretely, delegation means that an object transparently forwards client requests to a hidden delegate object, which may be dynamically changed:
There are several styles of delegation. The parameterized style assumes a reference to the handle will be passed to the body's member functions:
abstract class State {
abstract void serviceA(Context c);
abstract void serviceB(Context c);
abstract void serviceC(Context c);
}
Alternatively, Body can be an interface:
interface Body {
void serviceA(Context c);
void serviceB(Context c);
void serviceC(Context c);
}
The parameterless style assumes the Body base class maintains a reference to its Handle:
abstract class Body {
State(Handle h) { handle = h; }
abstract void serviceA();
abstract void serviceB();
abstract void serviceC();
protected Handle handle;
};
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 Body1 extends Body {
Body1(Handle h) { super(h); ... }
void serviceA() { ... }
void serviceB() { ... }
void serviceC() { ... }
}
Many behavioral patterns use delegation.