Composite Design Pattern in C#

This is the fourth post in the Structural Design Patterns series and in this post, we will discuss the Composite Design Pattern and its implementation using the C# progamming language.

The Composite Design Pattern provides us a way to treat every object (leaf and node) in a tree-like structure with uniformity. The Composite Design Pattern falls into the Structural Design Pattern category as defined by the Gang of Four (GoF). If you are new to design patterns, I would highly recommend you to read the introductory post “Introduction to Design Patterns

What is Composite Design Pattern?

The Composite Design Pattern allows us to have a tree-like structure forming a hierarchy and lets its client treat individual objects (leaf) and compositions of objects (nodes) with uniformity.

In simple terms, the pattern allows us to have a tree structure where every node can be expected to perform an identical (similar) task. Here in the tree structure, the leaf represents a primitive type that does not have children and is able to perform the assigned task. Whereas the nodes in the tree are a composite class whose children can be either be a composite class or a leaf node and it can also perform a similar assigned task. The intent of this pattern is that all the individual objects in a tree are treated the same way.

When to consider Composite Design Pattern

Few of the scenarios when the Composite Design Pattern can be considered as follows:

  • If we have a hierarchy of objects and all the objects participating in it should be treated similarly.
    • For example, let us assume we have an organization and here all the employees form a hierarchy (a tree structure). The organization chooses to share its monthly profit equally with every employee. Here applying this pattern will be ideal as the profit-sharing operation would be applied to all employees irrespective of the role they play in the organization.
  • The consumer or the client does not differentiate or does not know if it dealing with the composite object or an individual object and need to perform a similar operation on each of them.
    • Taking the above example of employees of an organization. Every employee is supposed to contribute some working hours. The client collects this information at the end of the day from every employee.

Composite Design Pattern – UML

composite design pattern UML diagram
Composite Design Pattern – UML

The artifacts of the UML are as follows:

  • Component: This the interface or the abstract class that defines the common functions that need to be implemented across the primitive (leaf) and the composite objects.
  • Leaf: This is the primitive type and implements the default behaviors as defined by its base Component interface (or abstract class). As its name suggests it the leaf and does not contain a reference to any composite type.
  • Composite: The composite contains other composite objects and implements the base component method. The composite class also contains methods related to child object management.
  • Client: The client uses the Component interface to access composite types and to perform the defined operations uniformly across the hierarchy.

C# Implementation

Let us implement the composite design pattern using a simple console application with the below-mentioned requirement.

Requirement Description

Let us assume we have a have an Organization having different departments and each department consists of employees. The requirement is that the Client wants to collect (read) the recorded working hours of every employee along with the total working hour of each department.

An organization maintains the list of employees in an hierarchical way, so we can consider the use of composite design pattern to fulfill the client’s requirement.

Requirement Implementation in C#

The artifacts of the implementation are as follows:

WorkHourComponent.cs: This is the base Component class that declare the common functionality of GetWorkingHour() for both the Employee (primitive) and the Organization (composite type) objects.

The class also contains the tree child management APIs – Add and Remove and the same will be ignored for the Employee (leaf) components

/// <summary>
    /// This is the base Component class that declare the common functionality of GetWorkingHour for both the 
    /// Employee (primitive) and the Organization (composite type) objects. The class also contains the tree child 
    /// management API - ADD and Remove and the same will be ignored for the Employee (leaf) components
    /// </summary>
    public abstract class WorkHourComponent
    {
        public WorkHourComponent()
        {

        }

        public string Name { get; set; } 
        
        public abstract int GetWorkingHour();

        public virtual void Add(WorkHourComponent component)
        {
            throw new NotImplementedException();
        }

        public virtual void Remove(WorkHourComponent component)
        {
            throw new NotImplementedException();
        }  
    }

Employee.cs: This is an Employee class and represents the Leaf or the Primitive type in the tree structure that cannot have children. This class implements the abstract WorkHourComponent class and overrides the GetWorkingHour() method to return the working hour recorded for the employee.

/// <summary>
    /// This is an Employee class and represent the Leaf or the Primitive type in the tree structure
    /// that cannot have children. This class implements the abstract WorkHourComponent class
    /// and overrides the GetWorkingHour() method to return the working hour recorded for the employee
    /// </summary>
    public class Employee : WorkHourComponent
    {
        public int Id { get; set; }
        public int WorkHour { get; set; } 
        public override int GetWorkingHour()
        {
            Console.WriteLine($"Employee Id# {Id} - {Name} recorded {WorkHour} work hours");
            return WorkHour;
        } 
    }

Organization.cs: This is the composite class and represents the complex (composite) objects that can have Employee (leaf) and other composites as their children.

/// <summary>
    /// This is the composite class and represents the complex (composite) objects that can have Employee (leaf) 
    /// and other composite as its children
    /// </summary>
    public class Organization : WorkHourComponent
    {
        protected List<WorkHourComponent> branches = new List<WorkHourComponent>();

        public override void Add(WorkHourComponent component)
        {
            branches.Add(component);
        }

        public override void Remove(WorkHourComponent component)
        {
            branches.Remove(component);
        }

        /// <summary>
        /// This is the common functionality
        /// </summary>
        /// <returns></returns>
        public override int GetWorkingHour()
        {
            var branchWorkingHours = 0;

            foreach (var branch in branches)
            {    
                branchWorkingHours += branch.GetWorkingHour();
            }

            Console.WriteLine($"{Name} recorded a total of [{branchWorkingHours}] Hours");
            Console.WriteLine();
            return branchWorkingHours;
        }
    }

Client.cs: This is the consumer of the component and invokes the GetWorkingHour() for all the object irrespective whether they are a primitive type or a composite type.

 class Client
    {
        static void Main(string[] args)
        {
            Organization organization = new Organization { Name = "Great Organization" };

            Organization itDepartment = new Organization { Name = "IT Department" };
            itDepartment.Add(new Employee { Id = 1001, Name = "John Doe", WorkHour = 8 });
            itDepartment.Add(new Employee { Id = 1002, Name = "Michelle Ritzwick", WorkHour = 6 });
            itDepartment.Add(new Employee { Id = 1005, Name = "Sabir Bhatia", WorkHour = 8 });

            Organization financeDepartment = new Organization { Name = "Finance Department" };
            financeDepartment.Add(new Employee { Id = 1003, Name = "Sunny Wenk", WorkHour = 7 });
            financeDepartment.Add(new Employee { Id = 1004, Name = "Stuart Board", WorkHour = 5 });

            organization.Add(itDepartment);
            organization.Add(financeDepartment);

            organization.GetWorkingHour();        
        }
    }

Application Output

composite design pattern sample application output
Composite Design Pattern – Application Output

Source Code

The source code can be found and downloaded from GitHub location: https://github.com/technicalbundle/CompositeDesignPattern

Advantages of Composite Design Pattern

Few of the advantages of using Composite Design Pattern are as follows:

  • The pattern helps to achieve uniformity (use of similar functions) across the object hierarchy that contains primitive as well as composite object types.
  • The pattern makes it easier to add new kinds of components.
  • The pattern makes it easier for the client to achieve the desired functionality without worrying about what kind of object is it dealing with.

Disadvantages of Composite Design Pattern

Few of the disadvantages of using Composite Design Pattern are as follows:

  • The composite pattern can become too general sometimes because of its uniformity, as for example, it is difficult to restrict objects that can be included in the composite group.
  • The client must be able to distinguish between composite and non-composite objects as the composite class is mostly extended to provide access to its individual group members in the hierarchy.

Conclusion

In this blog post, we try to learn about the Composite design pattern, where it can be applied along with its advantages and disadvantages. Overall, it is a great design pattern and come really handy when multiple objects need to be treated in the same way.

I hope you found this post helpful. Thanks for visiting. Cheers!!!

[Further Readings: Facade Design Pattern in C# |  Proxy Design Pattern in C# |  SQLite Studio to manage SQLite databases |  Adapter Design Pattern in C# |  How to use Blazor EditForm for Model Validation |  How to add a new profile in Windows Terminal |  How to easily Customize Global Settings in Windows Terminal |  How to add Git Bash to Windows Terminal Application |  How to customize Windows Terminal Application |  How to customize Windows Terminal Key Bindings |  How to Install Windows Terminal in Windows 10 |  Important Debugging Shortcuts of Visual Studio 2019 ]  

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
oldest
newest most voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x