slide: Appendix
The Handle/Body Idiom
introduction
framework
patterns
components
conclusions
references
appendix
experience
The handle/body class
idiom [Coplien,Eliens95,GOF,Stroustrup,Pree] separates the class defining a component's
abstract interface (the handle class) from its
hidden implementation (the body class). All intelligence is
located in the body, and (most) requests to the handle object
are delegated to its implementation.
In order to illustrate the several idioms,
we use the following class as a running example:
class A {
public:
virtual void f1() { cout << "A::f1() "; f2(); }
virtual void f2() { cout << "A::f2()" << endl; }
};
The implementation of A is straightforward and does not
make use of the handle/body idiom.
A call to the {\tt f1()} member function of A will print a message and
make a subsequent call to {\tt f2()}.
Without any modification in the behavior of A's instances, it is possible
to re-implement A using the handle/body idiom. The member functions of
class A are implemented by its body, and A is reduced to a simple
interface class:
class A { // Handle class delegating to its body
public:
A(); // defined later
virtual ~A() { delete body; }
virtual void f1() { body->f1(); }
virtual void f2() { body->f2(); }
private: A* body;
protected: A(int dummy); // used by body
};
Note that the implementation of A's body
can be completely hidden from the application programmer.
In fact, by declaring A to be the superclass of its body class,
even the existence of a body class can be hidden. If A is a class
provided by a shared library, new implementations of its body class
can be plugged in, without the need to recompile dependent applications:
class BodyOfA: public A {
public:
BodyOfA();
void f1() { cout << "A::f1() "; f2(); }
void f2() { cout << "A::f2()" << endl; }
};
In this example, the application of the idiom has only two
minor drawbacks. First, in the implementation below,
the main constructor of A
makes an explicit call to the constructor of its body class.
As a result, A's constructor
needs to be changed whenever an alternative implementation of the
body is required. The {\em Abstract Factory\/} pattern described in
[GOF].
may be used to solve this problem in a generic
and elegant way.
Another (esthetic) problem is the need for the dummy constructor
to prevent a recursive chain of constructor calls:
BodyOfA::BodyOfA(): A(911) { } // Call dummy constructor of A
A::A(int dummy) { body = 0; } // to build an abstract instance of A
A::A() { body = new BodyOfA; } // to build a concrete instance of A