Design Patterns
- Composite Pattern 2020
bogotobogo.com site search:
Composite Pattern
Intent
Composite objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects uniformly.
Composite Pattern - sample 1
#include <vector> #include <iostream> #include <algorithm> #include <functional> using namespace std; class Graphic { public: virtual void draw() const = 0; virtual void remove(Graphic *g) {} virtual void add(Graphic *g) {} virtual void getChild(int) {} virtual ~Graphic() {} }; class Line : public Graphic { public: void draw() const { cout << "Line draw()\n"; } }; class Rectangle : public Graphic { public: void draw() const { cout << "Rectangle draw() \n"; } }; class Text : public Graphic { public: void draw() const { cout << "Text draw() \n"; } }; // Composite class Picture : public Graphic { public: void draw() const { // for each element in gList, call the draw member function for_each(gList.begin(), gList.end(), mem_fun(&Graphic;::draw)); } void add(Graphic *aGraphic) { gList.push_back(aGraphic); } private: vector<Graphic*> gList; }; int main() { Line line; line.draw(); Rectangle rect; rect.draw(); Text text; text.draw(); Picture pic; pic.add(&line;); pic.add(▭); pic.add(&text;); pic.add(▭); pic.draw(); return 0; }
Output:
Line draw() Rectangle draw() Text draw() Line draw() Rectangle draw() Text draw() Rectangle draw()
Here is the summary of the composite pattern.
- The Composite pattern allows us to build structures of objects in the form of trees that contain both composition of objects and individual objects as nodes.
- Using a composite structure, we can apply the same operations over both composites and individual object. In other words, in most cases we can ignore the differences between composition of objects and individual objects.
line.draw(); // individual object pic.draw(); // compositions of objects
- The client uses the Component interface, draw() interface to manipulate the objects in the composition.
- The Component defines an interface for all objects in the composition both the composite (Picture) and the leaf (Line, Rectangle, and Text) nodes.
- The Component may implement a default behavior for add(), remove(), getChild() and its operation.
- Note that the Leaf also inherits methods like add(), remove(), and getChild(), which don't necessarily make a lot of sense for a leaf node.
- A leaf defines the behavior for the elements in the composition. It does this by implementing the operations the Composite supports.
- The composite's role is to define behavior of the components having children and to store child components.
- The composite also implements the Leaf-related operations.
Composite Pattern - sample 2
In this example, we have two sub-classes: SimpleNode class which has limited features while the ComplextNode class has more complete set of functions such as append_node(), copy_children(), and remove_node etc.
#include <iostream> #include <algorithm> #include <cassert> #include <functional> #include <iterator> #include <list> #include <string> using namespace std; class Node { public: explicit Node(const std::string &n;) : m_name(n) { assert(!n.empty()); } const std::string& get_name() const { return m_name; } void set_name(const std::string &n;) { assert(!n.empty()); m_name = n; } virtual Node* copy() const = 0; virtual ~Node() { } std::string m_name; private: Node(const Node&); Node& operator=(const Node&); }; class SimpleNode : public Node { private: SimpleNode(const std::string &n;, const std::string &v;) : Node(n), m_value(v) { assert(!n.empty()); } public: static SimpleNode* create(const std::string &n;, const std::string &v;) { return new SimpleNode(n, v); } const std::string& get_value() const { return m_value; } void set_value(const std::string &v;) { m_value = v; } SimpleNode* copy() const { assert(!get_name().empty()); return SimpleNode::create(get_name(), get_value()); } private: std::string m_value; SimpleNode(const SimpleNode&); SimpleNode& operator=(const SimpleNode&); }; class ComplexNode : public Node { private: explicit ComplexNode(const std::string &n;) : Node(n) { assert(!n.empty()); } template<typename ITER> ComplexNode(const std::string &n;, ITER b, ITER e) : Node(n), m_child_nodes(b, e) { assert(!n.empty()); } public: static ComplexNode* create(const std::string &s;) { return new ComplexNode(s); } template<typename ITER> static ComplexNode* create(const std::string &s;, ITER b, ITER e) { return new ComplexNode(s, b, e); } typedef std::list<Node*> ChildNodes; const ChildNodes& get_child_nodes() const { return m_child_nodes; } void append_node(Node *n) { assert(n != 0); m_child_nodes.push_back(n); } void remove_node(Node *nd) { assert(nd != 0); ChildNodes &c; = m_child_nodes; ChildNodes::iterator i = std::find(c.begin(), c.end(), nd); if (i != c.end()) { c.erase(i); } } ComplexNode* copy() const { assert(!get_name().empty()); ChildNodes c; copy_children(c); return ComplexNode::create(get_name(), c.begin(), c.end()); } ~ComplexNode() { const ChildNodes &c; = get_child_nodes(); ChildNodes::const_iterator b = c.begin(); for ( ; b != c.end(); ++b) delete *b; } private: void copy_children(ChildNodes &c;) const { assert(c.empty()); const ChildNodes &t; = get_child_nodes(); std::transform(t.begin(), t.end(), std::back_inserter(c), std::mem_fun(&Node;::copy)); assert(c.size() == t.size()); } ChildNodes m_child_nodes; ComplexNode(const ComplexNode&); ComplexNode& operator=(const ComplexNode&); }; int main() { SimpleNode *sa = SimpleNode::create("simpleA", "sA"); SimpleNode *sb = SimpleNode::create("simpleB", "sB"); ComplexNode *ca = ComplexNode::create("cmplxA"); ComplexNode *cb = ComplexNode::create("cmplxB"); ComplexNode *cc = ComplexNode::create("cmplxC"); ca->append_node(cb); ca->append_node(cc); cb->append_node(sa); cb->append_node(sb); list<Node*> nodes; nodes.push_back(ComplexNode::create("cmplx1")); nodes.push_back(ComplexNode::create("cmplx2")); list<Node*>::const_iterator b = nodes.begin(), e = nodes.end(); ComplexNode *cd = ComplexNode::create("cmplx3", b, --e); return 0; }
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization