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
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 of 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.
// 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.
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); |
CComposite<> does not make any assumptions about the allocation of the children, and therefore does not deallocate them upon destruction of the object.
That means that in Example 12, the objects that were allocated using the new() operator should be deallocated by some external agent before the root of the composite gets destroyed. Otherwise, a memory leak will occur.
Copyright © Rogue Wave Software, Inc. All Rights Reserved.
The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.