Friday, January 12, 2024

Composite Design Pattern in C#

The Composite Pattern is a structural design pattern that enables us to treat individual and group objects uniformly by creating a hierarchical (tree-like) structure of objects, where both the composite (groups) objects and leaf (individual) objects share a standard interface.

This pattern lets clients treat individual objects and compositions of objects uniformly. That means the client can access the individual objects or the composition of objects in a uniform manner. It’s useful for representing hierarchical structures such as file systems, UI components, or organizational structures.

What are Composite and Leaf classes?

  1. The Composite Class — represents a group of objects and can contain other objects (i.e. env_item_collection, env_item_collection_parent, env_root), and
  2. the Leaf Class — represents an individual object that cannot contain other objects (i.e. env_item).

Component of Composite Design Pattern

  • Component Interface: Define an interface or abstract class for implementing the composites and leaf nodes.
  • Leaf: Implement the component interface for the leaf nodes with no children.
  • Composite: Implement the component interface and also include a collection of components. The composite object can add, remove, and access the child components.
  • Client Code: The client works with all elements through the component interface.

Example in C#

Here’s an example in C# to illustrate the Composite Design Pattern.Let's consider a scenario where we have a company's organizational structure that includes employees and groups of employees.

Step1: Creating Component Interface(IEmployee.cs)

Create an interface named IEmployee.cs and copy and paste the following code.

namespace CompositeDesignPattern.ComponentInterface
{
    /// <summary>
    /// Component - The base interface or abstract class for all the objects in the composition
    /// </summary>
    public interface IEmployee
    {
        void DisplayDetails();
    }
}

  
Step2: Creating Leaf Class(Employee.cs)

The Leaf class represents the end objects, i.e., the object that does the actual work. A leaf can’t have any children. So, create a class file with the name Employee.cs and copy and paste the following code.

using CompositeDesignPattern.ComponentInterface;

namespace CompositeDesignPattern.Leaf
{
    /// <summary>
    /// Leaf - Represents individual employees
    /// </summary>
    public class Employee : IEmployee
    {
        private readonly string _name;
        private readonly string _position;

        public Employee(string name, string position)
        {
            _name = name;
            _position = position;
        }

        public void DisplayDetails()
        {
            Console.WriteLine($"Employee: {_name}, Position: {_position}");
        }
    }
}
  
Step3: Creating Composite Class(EmployeeGroup.cs)

The Composite class represents the composite components that have children. The Composite objects delegate the actual work to their children and then combine the results. So, create a class file named EmployeeGroup.cs and copy and paste the following code.

using CompositeDesignPattern.ComponentInterface;

namespace CompositeDesignPattern.Composite
{
    /// <summary>
    /// Composite - Represents a group of employees
    /// </summary>
    public class EmployeeGroup : IEmployee
    {
        private readonly List<IEmployee> _employees = new List<IEmployee>();

        public void AddEmployee(IEmployee employee)
        {
            _employees.Add(employee);
        }

        public void DisplayDetails()
        {
            foreach (var employee in _employees)
            {
                employee.DisplayDetails();
            }
        }
    }
}
  
Step4: Client(Program.cs)
using CompositeDesignPattern.Composite;
using CompositeDesignPattern.Leaf;

// Create individual employees
var employee1 = new Employee("Alice", "Manager");
var employee2 = new Employee("Bob", "Developer");
var employee3 = new Employee("Charlie", "Designer");

// Create a group and add employees to it
var group1 = new EmployeeGroup();
group1.AddEmployee(employee1);
group1.AddEmployee(employee2);

var group2 = new EmployeeGroup();
group2.AddEmployee(employee3);

// Create a larger group and add the previously created groups to it
var largerGroup = new EmployeeGroup();
largerGroup.AddEmployee(group1);
largerGroup.AddEmployee(group2);

// Display details of the larger group, which recursively displays all employees
largerGroup.DisplayDetails();
Console.ReadKey();
  

In the above example, the IEmployee interface is the component that both the leaf (Employee) and composite (EmployeeGroup) classes implement. Employee represents individual employees, while EmployeeGroup represents a group of employees. The EmployeeGroup class can contain either individual Employee objects or other EmployeeGroup objects, forming a tree-like structure.

When DisplayDetails() is called on the larger group, it traverses through the structure, displaying details of all individual employees within the groups.

Output

This pattern allows treating individual objects and compositions of objects uniformly, making it easier to work with complex tree structures.

The full source code is available here:

Happy coding!! 😊

No comments:

Post a Comment

^ Scroll to Top