Stingray® Foundation : Chapter 4 Design Patterns : The Composite Pattern
The Composite Pattern
The composite pattern composes objects into tree structures to represent part-whole hierarchies. The composite pattern lets client code treat individual objects and compositions of objects uniformly. For example, a composite shape is made up of several individual shapes such as rectangles and ellipses. The composite pattern allows simple shapes and complex shapes to be handled the same way.
The CComposite template class provides an implementation of the composite pattern. It maintains a list of child objects that are accessed through methods such as AddChild(), RemoveChild(), and GetChildrenCount(). In addition to having a list of children, each composite object maintains a pointer to its parent. The declaration of this templated class is shown in Example 10
Example 10 – CComposite class declaration
template <typename _Component, const GUID* _guid>
class CComposite:
public IQueryGuid
{
<…>
};
The first parameter passed into the CComposite template is the component type, which determines the type of parent and child objects in the composite. Objects in the tree are accessed using that type. For example, the declaration of the GetParent() method returns a pointer to a _Component object, not a CComposite<> object:
 
_Component* GetParent() const;
The second parameter in the template is a GUID that will identify the composite interface within the set of interfaces implemented by the component classes. The composite implementation does not assume an inheritance relationship between the CComposite<> class and the _Component class. Rather than an implicit conversion, casting from one to the other is performed using the guid_cast<> mechanism, standard in SFL. Whenever the CComposite<> interface is needed in some operation, a guid_cast<> is performed using the GUID passed in the second template parameter. Derived classes are responsible for providing an adequate interface map that allows this guid_cast<> call to succeed. All classes that mix in the CComposite<> template among their base classes are indirectly deriving from IQueryGuid, as seen earlier in Example 10.
Example 11 uses the CComposite class to compose complex shapes from simple shapes. The sample defines an entire class hierarchy that mixes in the composite pattern for all of its classes.
Example 11 – Composing complex shapes
// Abstract base class for all shapes
class __declspec(uuid("ABDC16B0-5195-11d3-4D94-00C06F92F286")) Shape
{
public:
virtual Draw(CDC* pDC) = 0;
};
 
// Define a GUID to provide a way to downcast any shape to a
// composite shape
class __declspec(uuid("ABDC16B1-5195-11d3-4D94-00C06F92F286"))
CompositeShape;
 
 
// Default implementation for composite shapes
class CompositeShapeBase :
public Shape,
public CComposite<Shape, __uuidof(CompositeShape)>
{
public:
virtual Draw(CDC* pDC)
{
// Iterate over list of contained shapes
// using the CComposite<> interface facilities
. . .
// Draw each child shape
. . .
}
 
BEGIN_GUID_MAP(CompositeShapeBase)
GUID_ENTRY_IID(__uuidof(CompositeShape), _compositeBase)
GUID_ENTRY_IID(__uuidof(Shape), _compositeBase)
END_GUID_MAP()
};
 
// A normal composite shape
 
class CompositeShapeNormal : public CompositeShapeBase
{
public:
virtual Draw(CDC* pDC)
{
// Draw shapes front to back
}
};
 
// A reverse Z-order composite shape
 
class CompositeShapeRev : public CompositeShapeBase
{
public:
virtual Draw(CDC* pDC)
{
// Reverse order and draw shapes back to front
}
};
 
// Simple shapes
 
class Polygon : public Shape
{
public:
virtual Draw(CDC* pDC)
{
// Draw a polygon
. . .
}
BEGIN_GUID_MAP(ShapeImpl)
GUID_ENTRY_IID(__uuidof(Shape), _compositeBase)
END_GUID_MAP()
};
 
class Rectangle : public Shape
{
public:
virtual Draw(CDC* pDC)
{
// Draw a rectangle
}
BEGIN_GUID_MAP(ShapeImpl)
GUID_ENTRY_IID(__uuidof(Shape), _compositeBase)
END_GUID_MAP()
};
 
Example 12 sets up a composite tree with three descendents, one of which is, in turn, a composite.
Example 12 – Setting up a composite tree
typedef CComposite<Shape, __uuidof(CompositeShape)> CompositeShape;
Shape* pRootShape = new CompositeShapeNormal;
CompositeShape* pComposite = guid_cast<CompositeShape>(pRootShape);
pComposite->AddChild(new Rectangle);
pComposite->AddChild(new Polygon);
Shape* pSubShape = new CompositeShapeRev;
CompositeShape* pSubComposite =
guid_cast<CompositeShape>(pSubShape);
pSubComposite->AddChild(new Rectangle);
pComposite->AddChild(pSubComposite);
NOTE >> CComposite<> does not make any assumptions about the allocation of the children, and therefore does not deallocate them upon destruction of the object.
In Example 12, then, the objects allocated using the new() operator should be deallocated by some external agent before the root of the composite gets destroyed. Otherwise, a memory leak occurs.