Composite is a classic Gang of Four pattern. It is probably one of the most aesthetically pleasing patterns in the catalog, due to the interplay it engenders between the static inheritance structure of its components and their dynamic runtime compositional relationships. As you might expect from its name, the Composite pattern exemplifies the principle: favor composition over inheritance.
The final sign that a Composite might be a suitable strategy in your code is the presence of, or a clear requirement for, a tree structure. This is in fact part of the Composite implementation, but some sets of components (GUIs, file system representations, XML elements) so obviously lend themselves to this kind of parent/child organization that the chances are you will already have thought about deploying it.
In our example we'll try to make use of an abstract GroupManager. Based on it, we'll expand two different types of classes: single workers and groups of workers (which can be single workers only, or mixed with other groups as well).
So, we start to define our abstract class with some useful methods:
Abstract Class GroupManager
abstract class GroupManager { protected $worker_name; // makes the difference between an empty group or a single worker protected $workersArr = array(); public function printName() { if (count($this->workersArr)) { foreach ($this->workersArr as $w) { $w->printName(); } } else { $name = ($this->worker_name) ? $this->worker_name : "Empty Group"; printf("%s\n", $name); } } public function addWorker(GroupManager $oneWorker) { array_push($this->workersArr, $oneWorker); } public function getWorkersCount() { $total = 0; if (count($this->workersArr)) { foreach ($this->workersArr as $worker) { $total += $worker->getWorkersCount(); } } else { $total = ($this->worker_name) ? 1 : 0; } return $total; } public function setName($name) { $this->worker_name = $name; } }
Now, we implement this in two different classes, also similar (the difference is in the value of the $name variable):
SingleWorker class
class SingleWorkerClass extends GroupManager { function __construct($name) { parent::setName($name); } } class MultiWorkerClass extends GroupManager { function __construct($name = "") { parent::setName($name); } }
And some real usage of it:
Usage
echo "SINGLE WORKER CASE: \n"; $single1 = new SingleWorkerClass("John Doe"); $single1->printName(); printf("How many: %d \n", $single1->getWorkersCount()); echo "SINGLE WORKER CASE: \n"; $single2 = new SingleWorkerClass("Jane Doe"); $single2->printName(); echo "CREATING GROUP OF 2: \n"; $multi1 = new MultiWorkerClass(); $multi1->printName(); $multi1->addWorker($single1); $multi1->addWorker($single2); $multi1->printName(); printf("How many: %d \n", $multi1->getWorkersCount()); echo "CREATING BIG GROUP (GROUP OF 2 + ONE SINGLE WORKER): \n"; $single3 = new SingleWorkerClass("Rambo"); $multi2 = new MultiWorkerClass(); $multi2->addWorker($multi1); $multi2->addWorker($single3); $multi2->printName(); printf("How many: %d \n", $multi2->getWorkersCount());