Allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Agnes has been working at Walmart for more than a year; she started as an inventory assistant and has recently been promoted as an inventory clerk. Agnes’s main job as an inventory clerk is to do daily stock recording. On her third day on the job, Agnes was approached by the manager with a concern. “Agnes, the inventory for the toy cars is inaccurate, not all boxes have the same number of cars inside. It is because some toy cars are bigger than the others so less cars will fit in the box.” After apologizing to the manager, Agnes went back to work. Being new to the job, she realized that this job was harder than she thought, she had to figure out a way to record the boxes accurately.
Below is how Product class looks like:
class Product
{
private $_name = null;
public function __construct($name)
{
$this->_name = $name;
}
public function getName()
{
echo $this->_name;
}
}
Below is how Box class looks like:
class Box
{
private $_products = array();
public function addProduct($products)
{
$this->_products = $products;
}
public function getProducts()
{
echo $this->_product;
}
}
Below is how Agnes’s job looks like:
class InventoryClerk
{
public function recordProducts(Box $box)
{
$products = $box->getProducts();
foreach($products as $product) {
$product->getName();
}
}
}
The entire inventory process seems to be pretty straightforward; however, that is not the case. In most cases, a big box consists of a dozen of small boxes, and in some cases, a product is packaged with a smaller box. This is quite a task for Agnes, and it often takes more time than it should because she has to sort everything out, creating a different category for the boxes according to their content and the amount of products within. This makes her work more tedious that I should be.
One possible solution to this problem is to place some conditions on the code. However, the packaging for the products may change in the future, and there is no way to tell what the actual contents of upcoming products will be. This makes the possible solution impractical and the codes difficult to maintain.
Agnes needs a better solution, a sustainable solution that is easy to maintain.
This is when the Composite Pattern comes into place. There is a part-whole hierarchy of objects in our case. A box may contain a box or a product. We can make the InventoryClerk class to treat Product object and Box object uniformly using the Composite Pattern.
In the Composite Pattern. there are four elements:
Let us rework our classes. First, we will need to create an interface ProductComponent:
interface ProductComponent
{
public function getName();
}
Rework Product class. It needs to implement ProductComponent interface:
class Product implements ProductComponent
{
private $_name = null;
public function __construct($name)
{
$this->_name = $name;
}
public function getName()
{
echo $this->_name;
}
}
Rework Box class. Beside implementing ProductComponent interface, it needs to have functions for adding, removing, and getting ProductComponent:
class Box implements ProductComponent
{
private $_productComponents = array();
public function getName()
{
$productComponents = $this->getProductComponents();
foreach($productComponents as $productComponent) {
$productComponent->getName();
}
}
public function addProductComponent(ProductComponent $productComponent)
{
$this->_productComponents[] = $productComponent;
}
public function removeProductComponent($i)
{
unset($this->_productComponents[$i]);
}
public function getProductComponents()
{
return $this->_productComponents;
}
}
Finally, rework InventoryClerk class; it has became very lean:
class InventoryClerk
{
public function recordProducts(ProductComponent $productComponent)
{
$productComponent->getName();
}
}
Now that we have applied the Composite Pattern to our case, our code has become much easier to maintain, and it is flexible too. Regardless of how many boxes there are within a box or no matter if the boxes have varying sizes and content, our code will work.
Let us take as an example two toys products wrapped in a small box individually and then the two small boxes are placed into a big box:
$productHelicopter` `= ``new` `Product(``'toy helicopter'``);
$productCar` `= ``new` `Product(``'toy car'``);
$smallBoxForHelicopter` `= ``new` `Box();
$smallBoxForHelicopter``->addProductComponent(``$productHelicopter``);
$smallBoxForCar` `= ``new` `Box();
$smallBoxForCar``->addProductComponent(``$productCar``);
$bigBox` `= ``new` `Box();
$bigBox``->addProductComponent(``$smallBoxForHelicopter``);
$bigBox``->addProductComponent(``$smallBoxForCar``);
For the Inventory Clerk, to record the big box, it will be as easy as this:
class InventoryClerk
{
public function recordProducts(ProductComponent $productComponent)
{
$productComponent->getName();
}
}
In our example, the Composite Pattern allows us to compose objects (Product objects and Box objects) into tree structures to represent part-whole hierarchies. Composite lets clients (InventoryClerk objects) treat individual objects (Product objects and Box objects) and compositions of objects uniformly (via ProductComponent abstraction).
Hopefully this simple tutorial helped you with your development. If you like our post, please follow us on Twitter and help spread the word. We need your support to continue. If you have questions or find our mistakes in above tutorial, do leave a comment below to let us know.