Composite Design Pattern in C#

Composite Design Pattern in C# with Examples

In this article, I will discuss the Composite Design Pattern in C# with Examples. Please read our previous article discussing the Bridge Design Pattern in C# with Examples. The Composite Design Pattern falls under the category of Structural Design Pattern. As part of this article, we will discuss the following pointers.

  1. What is the Composite Design Pattern in C#?
  2. Understanding the Composite Design Pattern in C# with Real-time Examples.
  3. Implementing the Composite Design Pattern in C#.
  4. Understanding the components involved in the Composite Design Pattern.
  5. When to use the Composite Design Pattern in real-time applications?
What is the Composite Design Pattern in C#?

According to the Gang of Four definitions, Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 

The Composite Design Pattern is a structural pattern that allows us to compose objects into tree structures to represent part-whole hierarchies. 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.

Example to Understand Composite Design Pattern in C#:

The term composite means a thing made of several parts or elements. Let us understand the Composite Design Pattern with one Real-Time Example. Here we want to assemble a computer. As we know, a computer comprises several parts or elements integrated together, as shown in the image below.

What is the Composite Design Pattern in C#?

As shown in the above image, everything is an object. So, here, a Computer, Cabinet, Peripherals, Hard Disk, Motherboard, Mouse, Keyboard, CPU, RAM, etc. all are objects.

A composite object is an object which contains other objects. You need to remember that a composite object may also contain other composite objects. The object that does not contain other objects is treated as a leaf object.

So, in our example, the Computer, Cabinet, Peripherals, and Mother Boards are composite objects, while the Hard Disk, CPU, RAM, Mouse, and Keyboard are the leaf objects shown in the diagram below.

Example to Understand Composite Design Pattern in C#

All the above objects are of the same type, i.e., Electronics. For example, if we talk about a computer, it is an electronic device. If we talk about the motherboard, it is an electronic device; similarly, if we talk about Mouse, it is also an electronic device. That means they follow the same structure and features. For example, all the above objects have a price. So, you may ask the price of a Keyboard or Mouse or Motherboard even though the price of a computer.

When you ask the price of the Keyboard, it should show you the price of the Keyboard. But when you ask the Price for the motherboard, it needs to display the Price of RAM and CPU. Similarly, when you ask the computer’s price, it needs to display all the prices.

The Composite Design Pattern is a tree structure with composite and leaf objects. The fundamental idea is that if you perform some operation on the leaf object, the same operation should also be performed on the composite objects. For example, if I can get the price of a Mouse, then I should be able to get the Price of Peripherals (here, it should display the price of both the Mouse and Keyboard), and even I should be able to get the Price of the Computer object. If you want to implement something like this, then you need to use the Composite Design Pattern.

Implementation of Composite Design Pattern in C#

Let us implement the above-discussed example using the Composite Design Pattern in C#. First, we will implement the above example using the Composite Design Pattern in C#. Then, we will discuss the UML Diagram of the Composite Design Pattern by comparing it with our example. The Composite Design Pattern Consists of the following components:

  • 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.
Step1: Creating IComponent Interface

Create an interface named IComponent.cs and copy and paste the following code. The following IComponent base interface declares the common operations for both simple and complex objects, i.e., the operations that will be common for both Leaf Objects and Composite Objects will be declared here. For our example, we need to show the price of a Leaf Component as well as we also need to show the Price of Composite Components. So, we need to declare that method here. As you can see in the below code, we have declared the  DisplayPrice method, which both Leaf Object and Composite Object will implement.

namespace CompositeDesignPattern
{
    // The base Component class declares the common operations for both simple and complex objects.
    public interface IComponent
    {
        void DisplayPrice();
    }
}
Step2: Creating Leaf Class

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 Leaf.cs and copy and paste the following code. This class implements the IComponent interface and provides implementations for the DisplayPrice method. As part of this class, we are creating two properties to display the component name and price, and we are initializing these two properties using the parameterized constructor of the class. And finally, we are displaying these two values in the DisplayPrice method.

using System;
namespace CompositeDesignPattern
{
    // The Leaf class represents the end objects. 
    // A leaf can't have any children.
    // The Leaf object is the Object which does the actual work
    public class Leaf : IComponent
    {
        public int Price { get; set; }
        public string Name { get; set; }

        public Leaf(string name, int price)
        {
            this.Price = price;
            this.Name = name;
        }

        public void DisplayPrice()
        {
            Console.WriteLine($"\tComponent Name: {Name} and Price: {Price}");
        }
    }
}
Step3: Creating Composite Class

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 Composite.cs and copy and paste the following code. As this class will be our composite class, we are creating a variable to hold the component name and another variable to hold its child components. Again, we are initializing the name property using the class constructor. Using the AddComponent() method, we add the child components inside the components variable. This class also implements the IComponent interface, and as part of the DisplayPrice() method, we call all the leaf object’s DisplayPrice method of the Composite Object using a for-each loop. 

using System.Collections.Generic;
namespace CompositeDesignPattern
{
    // The Composite class represents the complex components that have children. 
    // The Composite objects delegate the actual work to their children and then combine the result.
    public class Composite : IComponent
    {
        public string Name { get; set; }

        //The Object is used to hold all the child components of a composite components
        List<IComponent> components = new List<IComponent>();

        //The Constructor takes the Composite name as the input parameter
        public Composite(string name)
        {
            this.Name = name;
        }

        //The following Method is used to add Child Components inside the Composite Component
        public void AddComponent(IComponent component)
        {
            components.Add(component);
        }

        //Display the Price of Composite Components
        public void DisplayPrice()
        {
            foreach (var item in components)
            {
                //Delegates the work to the actual leaf object or child components
                item.DisplayPrice();
            }
        }
    }
}
Step4: Client Program

The client code works with all components (Both Leaf and Composite) via the base interface, i.e., IComponent, i.e., the class that implements the IComponent Interface. So, please modify the Main method of the Program class as shown below. Here, we create the tree structure and then show the respective component price by calling the DisplayPrice method. Here, you can see we are creating leaf objects, composite objects, and then calling the DisplayPrice method on both Leaf objects and Composite objects.

using System;
namespace CompositeDesignPattern
{
    public class Program
    {
        static void Main(string[] args)
        {
            // The client code works with all of the components (Both Leaf and Composite) via the base interface i.e. IComponent.
            // IComponent means the class that implements the IComponent Interface

            //Creating Leaf Objects or you can say child objects
            IComponent hardDisk = new Leaf("Hard Disk", 2000);
            IComponent ram = new Leaf("RAM", 3000);
            IComponent cpu = new Leaf("CPU", 2000);
            IComponent mouse = new Leaf("Mouse", 2000);
            IComponent keyboard = new Leaf("Keyboard", 2000);

            //Creating Composite Objects
            Composite motherBoard = new Composite("MotherBoard");
            Composite cabinet = new Composite("Cabinet");
            Composite peripherals = new Composite("Peripherals");
            Composite computer = new Composite("Computer");

            //Creating Tree Structure i.e. Adding Child Components inside the Composite Component
            //Adding CPU and RAM in Mother Board
            motherBoard.AddComponent(cpu);
            motherBoard.AddComponent(ram);

            //Adding Mother Board and Hard Disk in Cabinet
            cabinet.AddComponent(motherBoard);
            cabinet.AddComponent(hardDisk);

            //Adding Mouse and Keyboard in peripherals
            peripherals.AddComponent(mouse);
            peripherals.AddComponent(keyboard);

            //Adding Cabinet and Peripherals in Computer
            computer.AddComponent(cabinet);
            computer.AddComponent(peripherals);

            //To Display the Price of the Computer i.e. it will display the Price of all components
            Console.WriteLine("Price of Computer Composite Components");
            computer.DisplayPrice();

            //To display the Price of the Keyboard
            Console.WriteLine("\nPrice of Keyboard Child or Leaf Component:");
            keyboard.DisplayPrice();
            
            //To display the Price of the Cabinet
            Console.WriteLine("\nPrice of Cabinet Composite Component:");
            cabinet.DisplayPrice();

            Console.Read();
        }
    }
}
Output:

Composite Design Pattern in C# with Examples

Understanding the Class Diagram or UML Diagram of the Composite Design Pattern:

The Composite Design Pattern treats each node in two ways, i.e., Composite or Leaf. Composite means it can have other objects (both Composite or Leaf) below it. On the other hand, a Leaf node means there are no objects below it. Please look at the following diagram to understand the Class Diagram or UML Diagram of the Composite Design Pattern and understand the different components involved in the Composite Design Pattern.

Class Diagram or UML Diagram of the Composite Design Pattern

There are four participants involved in the Composite Design Pattern. They are as follows:

  1. Component: This will be an abstract class or interface containing the members that will be implemented by both Leaf and Composite Classes. If required, then it can also implement some of the behavior common to all objects in the composition, and in that case, we need to create the Component as an abstract class. That means the abstract class or interface acts as the base class for all the objects within the hierarchy. In our example, it is the IComponent interface.
  2. Leaf: This class will represent the leaf behavior in the composition. In our example, it is the Leaf class. A leaf object does not have any children.
  3. Composite: The Composite defines the behavior of the Composite Components. The component is having children. The children can be another composite component or can be a leaf component. In our example, it is the Composite class. This class defines the necessary operations that can be performed on the child components, i.e., Add, Remove, Get, Find, etc. methods.
  4. Client: The Client is the class that manipulates objects in the composition through the Component interface, i.e., the child classes that implement the Component interface or abstract class. In our example, the Program class is going to be the client.
Advantages of Composite Design Pattern:
  • Simplified Client Code: Clients can treat composite structures and individual objects uniformly, simplifying client code.
  • Clear Structure: Clearly defines the hierarchy or tree structure of complex objects.
  • Ease of Modification: Adding new kinds of components is easy as long as they support the same interface.
  • Flexibility in Design: The pattern provides flexibility to compose objects into tree structures to represent part-whole hierarchies.
When to use the Composite Design Pattern in C# Real-Time Applications?

The Composite Design Pattern in C# is particularly useful in scenarios where:

  • Hierarchical Tree Structures: When you need to represent a part-whole hierarchy. The pattern is ideal for situations where you are dealing with a tree structure with individual objects and compositions of objects treated uniformly.
  • Treating Individual and Composite Objects Uniformly: If you want to treat both individual objects and their compositions in the same way. This is useful when you want to ignore the difference between compositions of objects and individual objects.
  • Simplifying Client Code: It’s useful for simplifying client code, as it can treat composite structures and individual objects similarly, simplifying the client’s interaction with the structure.
  • Dynamic Configuration: When the configuration of the object structure can change at runtime, the Composite pattern allows for the dynamic addition or removal of components in the tree structure.
  • Graphic User Interfaces: In GUI development, you might have complex widgets composed of simpler components but want to treat them all as part of a uniform interface.
  • File System Representations: Representing file and directory structures, where directories can contain files and other directories, and you want to treat them all as a single file system entity.

In the next article, I will discuss the Real-Time Example of the Composite Design Pattern in C#. In this article, I try to explain the Composite Design Pattern in C# with Examples. I hope you enjoy this Composite Design Pattern in C# with Examples article.

1 thought on “Composite Design Pattern in C#”

  1. blank

    You wrote “The client code works with all of the components (Both Leaf and Composite) via the base interface i.e. IComponent i” But this is not true, because there is no AddComponent function in IComponent, and accordingly the subsequent call to DisplayPrice will be made through the inherited class but through abstraction.

Leave a Reply

Your email address will not be published. Required fields are marked *