The Composite design pattern is a structural design pattern that allows us to compose objects into a tree structure and then work with that structure as if it is a single object. That also means using this design pattern makes sense when the part of our app can be represented as a tree.

In this article, we are going to learn how to implement the Composite Design Pattern into our project and what are its benefits.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

The source code is available at the Composite Design Pattern – Source Code.

For the main page of this series check out C# Design Patterns.

Composite Design Pattern Structure

The Composite design pattern consists of the following parts:

  • Component
  • Leaf
  • Composite

A component is an interface that describes operations that are common to either simple or complex elements of the tree.

A leaf is a single object, that doesn’t have sub-elements. Our tree structure consists of more leaf objects.

A Composite is an object that does have sub-elements (leaves or other composite objects). Interesting thing is that the Composite object isn’t familiar with the concrete classes of its children. It communicates with its children via the Component interface.

Finally, we have a client, which works with all the elements through the Component interface.

But enough of the theory, let’s start with the concrete example.

Composite Design Pattern Implementation

Let’s imagine that we need to calculate the total price of a gift which we are selling in our shop. The gift could be a single element (toy) or it can be a complex gift that consists of a box with two toys and another box with maybe one toy and the box with a single toy inside. As we can see, we have a tree structure representing our complex gift so, implementing the Composite design pattern will be the right solution for us.

So, let’s start with the Component part:

public abstract class GiftBase
{
    protected string name;
    protected int price;

    public GiftBase(string name, int price)
    {
        this.name = name;
        this.price = price;
   }

    public abstract int CalculateTotalPrice();
}

We can see that our component consists of two protected fields and one abstract method. These fields and method are going to be used as an interface between the Leaf and the Composite part of our pattern.

Now, in many examples, we can see additional operations like add and remove inside the abstract class, but we are not going to add them in this class, because our Leaf class doesn’t need them. What we are going to create instead is a new interface – IGiftOperations:

public interface IGiftOperations
{
    void Add(GiftBase gift);
    void Remove(GiftBase gift);
}

Only our composite object will implement this interface, but the leaf object won’t. This is much better because our leaf object doesn’t need to implement the methods it won’t use.

The Composite Class Implementation

So, let’s continue on with the Composite class:

public class CompositeGift : GiftBase, IGiftOperations
{
    private List<GiftBase> _gifts;

    public CompositeGift(string name, int price)
        :base(name, price)
    {
        _gifts = new List<GiftBase>();
    }

    public void Add(GiftBase gift)
    {
        _gifts.Add(gift);
    }

    public void Remove(GiftBase gift)
    {
        _gifts.Remove(gift);
    }

    public override int CalculateTotalPrice()
    {
        int total = 0;

        Console.WriteLine($"{name} contains the following products with prices:");

        foreach (var gift in _gifts)
        {
            total += gift.CalculateTotalPrice();
        }

        return total;
    }
}

The class implementation is pretty straightforward. First, we have the GiftBase type list in which we store our Leaf or other Composite objects. We can add or remove those objects from our list by implementing Add and Remove methods from our IGiftOperations interface. Finally, we are calculating the total price of our Gift object with all the sub-gifts inside it.

The Leaf Class Implementation

Let’s continue on with the leaf part:

public class SingleGift : GiftBase
{
    public SingleGift(string name, int price)
        :base(name, price)
    {
    }

    public override int CalculateTotalPrice()
    {
        Console.WriteLine($"{name} with the price {price}");

        return price;
    }
}

And that is all we need for the Leaf implementation because it doesn’t have a sub-levels so it doesn’t require to add or remove features at all.

Finally, we can implement our client part:

class Program
{
    static void Main(string[] args)
    {
        var phone = new SingleGift("Phone", 256);
        phone.CalculateTotalPrice();
        Console.WriteLine();

        //composite gift
        var rootBox = new CompositeGift("RootBox", 0);
        var truckToy = new SingleGift("TruckToy", 289);
        var plainToy = new SingleGift("PlainToy", 587);
        rootBox.Add(truckToy);
        rootBox.Add(plainToy);
        var childBox = new CompositeGift("ChildBox", 0);
        var soldierToy = new SingleGift("SoldierToy", 200);
        childBox.Add(soldierToy);
        rootBox.Add(childBox);

        Console.WriteLine($"Total price of this composite present is: {rootBox.CalculateTotalPrice()}");
    }
}

We can see, that we are creating one gift with just one element inside it and one complex gift with the toys and an additional box with a single toy inside.

So, as soon as we run our app, we are going to get this result:

Composite - result

Excellent. That wraps it up for the Composite design pattern.

Conclusion

Even though this pattern might look a bit complex, it is very beneficial to use it when we have complex tree structures in our code.

Furthermore, by having a single interface that is shared by all the elements in the Composite pattern, the client doesn’t have to worry about the concrete class it works with.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!