By: J.O. Coplien
Published in: PLoPD4
Recasting of idioms in [Coplien92] into a pattern language that treats algebraic types. See an earlier version in [Coplien99a] and [Coplien99b]
Category: C++ Idioms
Summary: Recasting of idioms in [Coplien92] into a pattern language that treats algebraic types. See an earlier version in [Coplien99a] and [Coplien99b]
To separate interface from implementation in C++ objects, split a class into two implementation classes. One, the handle, becomes an identifier and is the user interface. The other, the body, is the implementation. The handle forwards member function calls to the body.
You're using Handle/Body. Assignment in C++ is defined recursively as member-by-member assignment with copying at the termination of the recursion. It would be more efficient if copying were rebinding. Add a reference count to the body class. Memory management is added to the handle class. Any operation that modifies the state of the body must make its own copy of the body and decrement the reference count of the original body.
To overcome the overhead of an additional level of indirection when applying Counted Body to immutable classes, associate a shared count and a separate shared body with each instance of a common handle abstraction.
You're using Handle/Body. Some classes have subtyping relationships and implementation-sharing relationships that do not correspond with each other. C++ ties implementation inheritance and representation inheritance together, and you may want to inherit each separately. Maintain separate inheritance hierarchies for handle classes and body classes. The base interface class contains a reference to the base implementation class.
You're using Handle/Body or Counted Body to support multiple implementations of a single ADT. Derive all solution body classes from a common base class. Use the handle class as the common base class for alternate bodies. Make handle member functions virtual. Each alternative implementation derived from the handle class overrides suitable virtual functions.
You're using Handle/Body. A client wants to create an object of an unspecified class in a class hierarchy. An object created from the classes in the hierarchy can be used interchangeably by the client. To create an object whose general type is known by the client but whose subtype characteristics must be chosen from context, use Envelope/Letter.
Your design enumerates system classes, and you need to establish the lifetime and scope of the objects for those classes. When should you use "new" to allocate an object? Objects that represent real-world entities that live outside the program should be instantiated using "new."
To construct the inheritance hierarchy for algebraic types, use Bridge [Gamma+95] to separate interface from implementation. The visible part is class Number. It contains a pointer to a representation part, which contains the representation and operations of the type, e.g., Complex, Real, Integer, Imaginary. Use State [Gamma+95] so Numbers can change type over time.
You're using Algebraic Hierarchy. You need to distribute addition to the objects. How many addition operations are there, and where do they belong? Each type should only support homogeneous algebraic operations unless performance dictates otherwise. Use Promote and Add
You're using Algebraic Hierarchy and Homogeneous Addition. To do heterogeneous addition, use run-time type identification (RTTI) to determine which of the two object types is more general. Promote and add the more specific type to the more general type using Promotion Ladder. Then use Homogeneous Addition. If one type is not a proper subtype of the other, use Non-Hierarchical Addition
You're using Promote and Add. Where do you put the knowledge of type promotion? Each class should know how to promote itself to its own base class type. Promotions of more than two levels of the inheritance hierarchy can be handled by multiple successive promotions.
You're using Promotion Ladder and Homogeneous Addition. Sometimes neither type is a subtype of the other, so neither can be promoted to the type of the other. Promote both to a more general type.
Promotion between objects of different but related C++ types--zero or one of which is a built-in type or a type exported by a library for which you do not have the source--promote a class object type to a built-in type or a type exported from a library using a member conversion operator. Use constructors for all other promotions.