design patterns

Structural Design Patterns: Composite

Today we’ll be continuing our exploration of the common Structural design patterns as part of our much bigger Guide to Software Design Patterns series. Next up is the composite design pattern, which is used to separate an abstraction from its implementation so both can be modified on their own. Specifically, the composite pattern aims to treat singular object instances and collections of the same object types uniformly. This allows the client code to interact with this type of composited object the same throughout the code, regardless of whether it’s a singular instance or a collection of instances.

Throughout this article we’ll look more closely at the composite pattern by examining both a real world example of its use and a fully-functional C# code example to show how you might put the composite pattern into practice. Let’s get to it!

In the Real World

A real world concept we can all relate to is that of family. Love ’em or leave ’em, we all have family members to some degree and we can examine the rough hierarchy of familiar ties as a way to explore how the composite design pattern can be seen in the real world.

You, just like your parents or your siblings or cousins, are an individual person. You have your own unique qualities and attributes that make you you, just as the other individual members of your family have their own quirks and idiosyncrasies. And yet, even though all our family members are unique individuals, we all make up the larger group of a family. Put another way, we might say that your family is composed of individual family members like yourself.

Expanding this idea further, family members are related to one another through their parentage. You and your siblings might share the same parent, your children will have you as a parent, while your mother had someone else as a parent, and so forth. Thus, all these individuals that were born into the same family tree are still a part of that overall composition of that family group.

How It Works In Code

As discussed in the introduction, the basic purpose of the composite pattern is to allow an object to represent both a singular and a collective group of said object type, without any need to differentiate between the two groupings when using them. To better understand this we’ll start with the full code example and then we’ll break it down a bit and see what’s actually happening and how the composite design pattern is being used:

In this example we’re continuing with the family concept. We want to be able to add parent/child relationships to our individuals while also maintaining a collection of children under the hood. To begin we start with a simple IFamilyMember interface that only provides a single Name property:

From there we add our Person class, which is a normal class but is also enumerable through the IEnumerable interface:

Our Person class implements our simple IFamilyMember interface and, therefore, has become the composite object of our example. In order to behave as a composite we need to provide basic operations within it that allow it to alter underlying children objects, which are stored privately in the _children property. _children is just a List<IFamilyMember>, so we can start to see the potential for recursion that we’re after.

To complete the capabilities we’ve added a few helper methods to Person like AddChild(), RemoveChild(), and GetChild(). We’ve also added the GetChildren() method to get the full list of children, plus a helper LogChildren() method to output the list so we can see what’s going on during testing.

With everything in place we can now try creating a few people and adding some relationships, like so:

We start by creating some new Person instances to represent the parents (Jane and John) and their children (Alice, Billy, and Christine). Before we do anything else we want to confirm that Jane and John don’t have any children so we output that and confirm:

Getting away from the nuclear family a bit, as it happens Jane and John have one child together (Alice), but each of them also have a child from before they got together (Billy for John and Christine for Jane). Thus when we LogChildren() after adding their respective kids we get a new output:

What makes the composite pattern particularly cool is that we can expand on the base interface that our object is using — IFamilyMember in this case — to create other types of components known as leaf components. In this case we’ve added the Aunt and Uncle leaf components, both of which must use the IFamilyMember interface:

This allows us to continue our example from above by introducing a few new people and relationships by using one of the leaf component classes:

Here we’ve added John's brother David. Since John has children this obviously makes David an Uncle so we use that leaf component class for him. Both David and John are the sons of Edward, so we also add him as another Person and then add both David and John as children. This is a critical benefit of the composite pattern: Even though David and John are actually different object types (Uncle and Person, respectively) they can both be added in the same way using the same AddChild() method of the Person composite object. We can confirm this all works with a call to edward.LogChildren():