Skip to content
Carmel Eve By Carmel Eve Software Engineer I
Design patterns in C# - The Composite Pattern

So, this week I'm moving on from the creational patterns (though I might return at some point) and moving on to the structural design patterns!

Structural design patterns are ones that deal with the relationships between different elements. Essentially, they define how different parts of the code interact with and relate to each other.

This week I'll be focusing on the composite pattern. This is a fairly simple pattern, which specifically defines the behaviour of objects which are made up of other objects – composite objects. The critical idea is that the group of objects should be treated the same as the individual objects it is composed of. This means that whether a group is made up of individual objects or more groups of objects, the same logic can be applied. There are many examples of this in the real world; One that sticks in my mind is around email groups. When you have a list of recipients, that list can be made up of both individual email addresses and of groups which contain many email addresses, and these groups can also contain child groups containing still more addresses. When you press send, these groups are treated the same as the individuals and everyone is sent the email.

But, in-keeping with the theme, I've also produced a dinosaur-related(ish) example!

So say we have an interface, IPlant, which has one method: Eat()

public interface IPlant
{
    void Eat();
}

The simplest implementation of IPlant is a Leaf:

public class Leaf : IPlant
{
    public bool IsEaten { get; private set; } = false;
    
    public void Eat()
    {
        IsEaten = true;
        Console.WriteLine("Leaf eaten!");
    }
}
The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

We then have our composite object, a Branch. A branch is made up of IPlants.

public class Branch : IPlant
{
    private readonly IList<IPlant> children;

    public Branch(IList<IPlant> children)
    {
        this.children = children;
    }

    public void Eat()
    {
        foreach (var child in children)
        {
            child.Eat();
        }
    }
}
The best hour you can spend to refine your own data strategy and leverage the latest capabilities on Azure to accelerate your road map.

This means that each Branch can be composed of a mixture of child branches and leaves. When Eat is called on the Branch, each of the IPlants it is made up of will then be eaten in turn. In this way each branch is treated the same as a leaf, with the "eat" action propagating down from branch to leaf, and eventually the whole structure being consumed!

Therefore, if we run the following code:

public static void CompositeExample()
{
    var plants = new List<IPlant>();

    var branch = new Branch(new List<IPlant>() { new Leaf(), new Leaf() });
    var anotherBranch = new Branch(new List<IPlant>() { new Leaf(), new Leaf(), new Leaf(), new Leaf() });

    plants.Add(new Branch(
        new List<IPlant>()
            { branch, anotherBranch }
    ));

    plants.Add(new Leaf());
    plants.Add(new Branch(new List<IPlant>() { new Leaf(), new Leaf(), new Leaf(), new Leaf(), new Leaf() }));
    plants.Add(new Leaf());

    foreach (IPlant plant in plants)
    {
        plant.Eat();
    }
}

We have a list which consists of:

  • A big branch, which is made up of 2 smaller branches, one with 2 leaves and one with 4.
  • A branch with 5 leaves
  • 2 individual leaves.

That's a total of 13 leaves.

So, when we Eat everything in the list, we will get the following output:

That's 13 leaves eaten! (Trust me I counted, twice.)

Thanks for reading, here's a link to the GitHub repository which contains all the code for this blog series. And watch of for my next blog on design patterns in C#!

Carmel Eve

Software Engineer I

Carmel Eve

Carmel is a software engineer and LinkedIn Learning instructor. She worked at endjin from 2016 to 2021, focused on delivering cloud-first solutions to a variety of problems. These included highly performant serverless architectures, web applications, reporting and insight pipelines, and data analytics engines. After a three-year career break spent travelling around the world, she rejoined endjin in 2024.

Carmel has written many blog posts covering a huge range of topics, including deconstructing Rx operators, agile estimation and planning and mental well-being and managing remote working.

Carmel has released two courses on LinkedIn Learning - one on the Az-204 exam (developing solutions for Microsoft Azure) and one on Azure Data Lake. She has also spoken at NDC, APISpecs, and SQLBits, covering a range of topics from reactive big-data processing to secure Azure architectures.

She is passionate about diversity and inclusivity in tech. She spent two years as a STEM ambassador in her local community and taking part in a local mentorship scheme. Through this work she hopes to be a part of positive change in the industry.

Carmel won "Apprentice Engineer of the Year" at the Computing Rising Star Awards 2019.