This is part of a series of posts demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Here's the series index page.

What Is This Pattern?

The Composite design pattern represents part-whole hierarchies of objects. "Part-whole hierarchies" is a really fancy way of saying you can represent all or part of a hierarchy by reducing the pieces in said hierarchy down to common components.

When using this pattern, clients should be able to treat groups of objects in a hierarchy as "the same" even though they can be different. You can do this selectively to parts of the hierarchy, or to the entire hierarchy.

The Rundown

  • Type: Structural
  • Useful? 4/5 (Very)
  • Good For: Treating different objects in a hierarchy as the same.
  • Example Code: On GitHub

The Participants

  • The Component declares an interface for objects in the composition. It also implements behavior that is common to all objects in said composition. Finally, it must implement an interface for adding/removing it's own child components.
  • The Leaves represent leaf behavior in the composition (a leaf is an object with no children). It also defines primitive behavior for said objects.
  • The Composite defines behavior for components which have children (contrasting the Leaves). It also stores its child components and implements the add/remove children interface from the Component.
  • The Client manipulates objects in the composition through the Component interface.

A Delicious Example

To model Composite effectively, let's think about a soda dispenser. Specifically, one of the Coca-Cola Freestyle machines you'll find at certain restaurants or movie theatres.

A Coca-Cola Freestyle soft drink dispenser.

For those of you that haven't seen these monstrosities, they're not at all like the regular soda dispensers you'll find at restaurants. The regular dispenses have six, or eight, or maybe twelve flavors; the Freestyle machines have potentially hundreds. Any flavor of drink that the Coca-Cola company makes in your part of the world, you can order at this machine.

The most interesting part of this device, though, is its interface. The Freestyle wants you to "drill-down" by first selecting a brand (e.g. Coke, Fanta, Sprite, Dasani, etc.) and then selecting a flavor (e.g. Cherry, Vanilla, etc.). In effect, this creates a hierarchy where "Soda" itself is the root Component; the brands are the child Components, and the flavors are Leaves.

A simplified version of this hierarchy might look like this:

A soft drink decision tree, with three second level nodes for Cola, Lemon-Lime, and Root Beer.  Cola and Root Beer each have two children representing their possible flavors.

Let's model this hierarchy. For all possible flavors of soda that our machine dispenses, we need to know how many calories each particular flavor has. So, in our abstract class that represents all soft drinks, we need a property for Calories:

/// <summary>
/// Soda abstract class
/// </summary>
public abstract class SoftDrink  
{
    public int Calories { get; set; }

    public SoftDrink(int calories)
    {
        Calories = calories;
    }
}

We also need to implement several Leaves for the concrete soda flavors (doesn't that sound disgusting?).

/// <summary>
/// Leaf class
/// </summary>
public class OriginalCola : SoftDrink  
{
    public OriginalCola(int calories) : base(calories) { }
}

/// <summary>
/// Leaf class
/// </summary>
public class CherryCola : SoftDrink  
{
    public CherryCola(int calories) : base(calories) { }
}

/// <summary>
/// Leaf class
/// </summary>
public class OriginalRootBeer : SoftDrink  
{
    public OriginalRootBeer(int calories) : base(calories) { }
}

/// <summary>
/// Leaf class
/// </summary>
public class VanillaRootBeer : SoftDrink  
{
    public VanillaRootBeer(int calories) : base(calories) { }
}

/// <summary>
/// Leaf class
/// </summary>
public class LemonLime : SoftDrink  
{
    public LemonLime(int calories) : base(calories) { }
}

We now need to implement the Composite participant, which represents objects in the hierarchy which have children. For our decision tree, we have two Composites: Colas and RootBeers.

/// <summary>
/// Composite class
/// </summary>
public class Colas  
{
    public List<SoftDrink> AvailableFlavors { get; set; }

    public Colas()
    {
        AvailableFlavors = new List<SoftDrink>();
    }
}

/// <summary>
/// Composite class
/// </summary>
public class RootBeers  
{
    public List<SoftDrink> AvailableFlavors { get; set; }

    public RootBeers()
    {
        AvailableFlavors = new List<SoftDrink>();
    }
}

Now, let's imagine we need to report the amount of calories in each of our flavors to our customers. The customers doesn't care about our hierarchy, they just wants to know how many calories each flavor has. Since we implemented the Composite design pattern, we can provide this data easily in our Component participant (which represents the soda dispenser itself):

/// <summary>
/// The Component class
/// </summary>
public class SodaDispenser  
{
    public Colas Colas { get; set; }
    public LemonLime LemonLime { get; set; }
    public RootBeers RootBeers { get; set; }

    public SodaDispenser()
    {
        Colas = new Colas();
        LemonLime = new LemonLime(190);
        RootBeers = new RootBeers();
    }

    /// <summary>
    /// Returns all available flavors and display their calories
    /// </summary>
    public void DisplayCalories()
    {
        var sodas = new Dictionary<string, int>();
        foreach (var cola in Colas.AvailableFlavors)
        {
            sodas.Add(cola.GetType().Name, cola.Calories);
        }
        sodas.Add(LemonLime.GetType().Name, LemonLime.Calories);

        foreach (var rootbeer in RootBeers.AvailableFlavors)
        {
            sodas.Add(rootbeer.GetType().Name, rootbeer.Calories);
        }

        Console.WriteLine("Calories:");
        foreach (var soda in sodas)
        {
            Console.WriteLine(soda.Key +": " + soda.Value.ToString() + " calories.");
        }
    }
}

Finally, our Main() shows how we might initialize a SodaDispenser with several hierarchical flavors and then display all of the calories for each flavor:

static void Main(string[] args)  
{
    SodaDispenser fountain = new SodaDispenser();
    fountain.Colas.AvailableFlavors.Add(new OriginalCola(220));
    fountain.Colas.AvailableFlavors.Add(new CherryCola(230));
    fountain.LemonLime.Calories = 180;
    fountain.RootBeers.AvailableFlavors.Add(new OriginalRootBeer(225));
    fountain.RootBeers.AvailableFlavors.Add(new VanillaRootBeer(225));
    fountain.DisplayCalories();

    Console.ReadKey();
}

The output of this method being:

A screenshot of the Composite pattern sample project, showing all of the calorie entries for each flavor in the hierarchy.

Will I Ever Use This Pattern?

Will you ever have hierarchical data? If so, probably yes. The key part of this pattern is that you can treat different objects as the same, provided you set up the appropriate interfaces and abstracts.

Summary

The Composite pattern takes objects in a hierarchy and allows clients to treat different parts of that hierarchy as being the same. Then, among other things, you can "flatten" all or part of the hierarchy to get only the data that's common to all of the parts.

As always, I like to provide code with my tutorials, so the repository for this pattern is over on GitHub and contains all of the sample code used here.

(For all you international soda drinkers out there, maybe you can help me with something. I was not aware that the flavor "root beer" was solely a North American thing, but Wikipedia says it is. Do you all not get this kind of soda in your country? Because it is the bomb and you should try it.)

Happy Coding!

Get My eBook FREE!

Did you enjoy this post? This article and 21 others are also in my free eBook, "The Daily Design Pattern", which you can get just by signing up for my email list.