Design Patterns - Composite

27 april 2022 om 10:00 by ParTech Media - Post a comment

In our previous blog of the design pattern series, we had discussed the Bridge design pattern, which falls under the category of structural design patterns. Today, we will explore another design pattern from the same category called Composite Design Pattern.

Table of contents

  1. What is Composite Design Pattern?
  2. Implementation of Composite Design Pattern
  3. Conclusion

Composite Design Pattern

By now, we all know that structural design patterns provide a flexible and efficient way to arrange objects in larger structures. And also, we have seen how the objects of different classes arrange themselves in a larger structure in the structural design pattern series. Today, in the composite design pattern, we will see how a group of objects is treated the same way as the single instance in the same type of object.

Composite design patterns represent the objects in a tree structure where hierarchies are followed. A tree structure can contain a branch node as well as a leaf node. Composite design patterns help in treating both of them equally, thus reducing the complexity.

Implementation of Composite Design Pattern

The Composite Design Pattern mainly consists of

  • Component
  • Composite
  • Leaf

To explain these terms, let us consider a tree. A tree has a stem that acts as a base for all its branches. Branches can have sub-branches or leaves. On a similar note,

  • The component is an abstract class/interface that contains the members that need to be implemented by all the objects in the hierarchy. It acts as a base based on which the derived class are implemented.
  • Composite is a class that can be a node by itself. It can have child nodes as well as a leaf.
  • A Leaf is a class that is the last in the hierarchy, where the tree structure ends.
using System;
using System.Collections.Generic;

namespace PARTECH_Composite
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var root = new Composite();
            
            var child1 = new Composite();
            var child1a = new Composite();
            var child2 = new Composite();

            var leaf1 = new Leaf();
            var leaf2 = new Leaf();
            var leaf3 = new Leaf();

            child1.AddChild(child1a);
            child1a.AddChild(leaf1);
            child2.AddChild(leaf2);
            
            root.AddChild(child1);
            root.AddChild(child2);
            root.AddChild(leaf3);

            root.DoAction();
            Console.ReadLine();
        }
    }

    public interface IComponent
    {
        public void DoAction();
    }

    public class Composite : IComponent
    {
        private List<IComponent> components = new List<IComponent>();

        public void AddChild(IComponent child)
        {
            components.Add(child);
        }

        public void RemoveChild(IComponent child)
        {
            components.Remove(child);
        }
        
        public void DoAction()
        {
            Console.WriteLine($"This Component has total { components.Count } child");

            components.ForEach(x => x.DoAction());
        }
    }

    public class Leaf : IComponent
    {
        public void DoAction()
        {
            Console.WriteLine("Reached Leaf");
        }
    }
}

The above code is a template for a composite design pattern. Where IComponent is an interface that holds the signature of the method DoAction. The composite class inherits IComponent and implements the method DoAction. It has a private property called components. And components can be added to that collection by using Add/Remove child methods. Leaf class inherits IComponent and implements the method DoAction. In the main method, a root object of the type component is created. Child objects of type component are created, and leaf objects are created. And they are added as respective children.

So root has two children and one leaf. The first child has one child, and it has a leaf. The second child has a leaf. And when the DoAction of the root object is called, it prints the entire set information and their count.

Let’s implement the Composite design pattern in a real-life scenario and understand it further. Let us consider a random organization and try to print its hierarchy. The organization has a CEO, CFO, and CTO. The CFO and CTO have individual subordinates. Both CFO and CTO report to the CEO. Let's implement this through code.

using System;
using System.Collections.Generic;

namespace PARTECH_Composite
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var ceo = new Composite(new Employee { Id = 1, Name = "CEO", ReportsTo = "" });

            var cfo = new Composite(new Employee { Id = 2, Name = "CFO", ReportsTo = "CEO" });
            var controller = new Composite(new Employee { Id = 3, Name = "Controller", ReportsTo = "CFO" });
            var financialAccountant = new Leaf(new Employee { Id = 4, Name = "Financial Accountant", ReportsTo = "Controller" });

            var cto = new Composite(new Employee { Id = 5, Name = "CTO", ReportsTo = "CEO" });
            var technicalDirector = new Composite(new Employee { Id = 6, Name = "Technical Director", ReportsTo = "CTO" });
            var teamLead = new Composite(new Employee { Id = 7, Name = "Team Lead", ReportsTo = "Technical Director" });

            ceo.AddSubOrdinates(cfo);
            ceo.AddSubOrdinates(cto);

            cfo.AddSubOrdinates(controller);
            controller.AddSubOrdinates(financialAccountant);

            cto.AddSubOrdinates(technicalDirector);
            technicalDirector.AddSubOrdinates(teamLead);

            ceo.DoAction();

            Console.ReadLine();
        }
    }


    public class Employee
    {
        public string Name { get; set; }
        public int Id { get; set; }
        public string ReportsTo { get; set; }
    }

    public interface IComponent
    {
        public void DoAction(string toAppend = null);
    }

    public class Composite : IComponent
    {
        private Employee employee;
        private List<IComponent> subOrdinates = new List<IComponent>();

        public Composite(Employee emp)
        {
            employee = emp;
        }

        public void AddSubOrdinates(IComponent Subordinate)
        {
            subOrdinates.Add(Subordinate);
        }

        public void DoAction(string toAppend = null)
        {
            if (employee != null)
            {
                Console.WriteLine($"{ toAppend ?? string.Empty }Details: Id - { employee.Id }, Name - { employee.Name }, ReportsTo - { employee.ReportsTo }");
                Console.WriteLine();
            }

            toAppend += "\t";

            subOrdinates.ForEach(x => {
                x.DoAction(toAppend);
                Console.WriteLine();
            });
        }
    }

    public class Leaf : IComponent
    {
        private Employee employee;

        public Leaf(Employee emp)
        {
            employee = emp;
        }

        public void DoAction(string toAppend = null)
        {
            Console.WriteLine($"{ toAppend ?? string.Empty }Details: Id - { employee.Id }, Name - { employee.Name }, ReportsTo - { employee.ReportsTo }");
        }
    }
}

Similar to the composite design pattern template, the above implementation has an IComponent, Composite, and a Leaf. Along with these, there is an employee class that is used to maintain the employee data.

In the main method, an object for CEO is created with related employee info. Then CFO and CTO details are created, and their respective sub coordinates are added. Finally, the DoAction method from the CEO object is called. When that happens, the below output is printed in the console.

Conclusion

And that’s what a Composite Structural design pattern is all about. In this post, we have also seen how to implement it in a real-world scenario. Stay tuned for the next blog on the Design Patterns series.