One of the most important structural patterns is the Composite one: its goal is managing a hierarchy of objects where both leaf objects and composition of other objects conform to a common interface. This hierarchy is usually constituted by part-whole relationships (often composition in the strict sense or aggregation), and it can be viewed as a tree.
The power of the Composite pattern resides in a Client which refers only to a Component interface, the common abstraction between all kinds of tree elements, and it is oblivious to changes in the underlying structure. The Client is not even aware of the hierarchical structure existence and it is passed a reference to the tree head.
Meanwhile, the Composite responsibility is to build on its Component children's operations, and propagate their calls towards the bottom of the hierarchical graph until they reach Leafs.
Participants:
- Client: sends message to the head Component.
- Component: declares the interface that various parts of the graph should respect.
- Leaf: concrete class that has no children.
- Composite: concrete class that composes other Components (no pun intended).
<?php /** * Component interface. * The Client depends only on this abstraction, whatever graph is built using * the specializations. */ interface HtmlElement { /** * @return string representation */ public function __toString(); } /** * Leaf sample implementation. * Represents an <h1> element. */ class H1 implements HtmlElement { private $_text; public function __construct($text) { $this->_text = $text; } public function __toString() { return "<h1>{$this->_text}</h1>"; } } /** * Leaf sample implementation. * Represents a <p> element. */ class P implements HtmlElement { private $_text; public function __construct($text) { $this->_text = $text; } public function __toString() { return "<p>{$this->_text}</p>"; } } /** * A Composite implementation, which accepts as children generic Components. * These children may be H1, P or even other Divs. */ class Div implements HtmlElement { private $_children = array(); public function addChild(HtmlElement $element) { $this->_children[] = $element; } public function __toString() { $html = "<div>\n"; foreach ($this->_children as $child) { $childRepresentation = (string) $child; $childRepresentation = str_replace("\n", "\n ", $childRepresentation); $html .= ' ' . $childRepresentation . "\n"; } $html .= "</div>"; return $html; } } // Client code $div = new Div(); $div->addChild(new H1('Title')); $div->addChild(new P('Lorem ipsum...')); $sub = new Div(); $sub->addChild(new P('Dolor sit amet...')); $div->addChild($sub); echo $div, "\n";Some notes on the implementation:
- evaluate if maintaining parent references in Component specializations would be a waste of time. This addition prevents sharing of objects as children of different Composites and makes the mutual reference management more complex.
- often also the remove() operation is not even required in php implementations, because of the transient nature of object graphs. It is rare to modify a tree unless if its objects are entities; service trees such as Chains of responsibility are built via configuration at the startup.
- the only operation which is really mandatory, besides the ones present in the Component interface, is a way to specify the children, via an add() method or the constructor.
- a Builder can encapsulate the mechanics of the construction process of the object tree.
I really enjoyed this post and all your previous posts about design patterns. There is one pattern i would like to know more about, its the observer pattern, perhaps you can write something about in the future ;)
ReplyDeleteIt would be treated as I go on in the Gang of Four patterns list :)
ReplyDeleteGood overview of DP. But are the examples really so "practical"? Would be nice to see solutions to everyday problems.
ReplyDeleteSometimes it's hard to think of real examples as patterns should not be forced in every common situation; but I keep presenting only full implementations, which can be pasted in a .php file and run instantly.
ReplyDeleteFor beginner like me it would be also useful to have one example without DP and then compare what we gain
ReplyDeleteHi, When called the function __toString() in Div class?
ReplyDeleteCan you explain in few word this last class? (Div)
Thanks.
In php the function __toString() is automatically called to cast an object to a string. This means that writing
ReplyDeleteecho $div;
is equivalent to writing:
echo $div->__toString();
I have an article about the Composite pattern for JavaScript on my blog JoeZimJS. I love design patterns and it's always good to see how they're implemented in different languages.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteVery nice post. I've been looking into this pattern to make sure I understood it 100%. I just wrote a brief example of it to try out my own implementation of it. Instead of using HTML as the application, I decided to use arithmetic operations. Here's my version of it: http://www.rodrigo-silveira.com/composite-design-pattern-php/ any thoughts?
ReplyDelete