SnapShooter Backups Server, Database, Application and Laravel Backups - Get fully protected with SnapShooter

Understanding Design Patterns - Composite

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:

  • Component: It is the interface that has both Left and Composite to implement. This class defines the abstract functions that the client uses. In our case, the function is getName().
  • Leaf: It is the class that has no child classes as its name implies. It implements Component interface as mentioned. In our case, Leaf is our Product class.
  • Composite: It composites abstract Component classes. So it can composite concrete Product class, Composite class or both, besides implementing Component interface. Composite also provides functions for adding, removing, and getting child components.
  • Client: It treats both Leaf and Composite classes uniformly by calling the common function defined in Component class.

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).

The end

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.