PTOM: The Composite Design Pattern


The Composite Design Pattern

This post talks about the Composite Design Pattern and is part of Pablo’s Topic of the Month – November: Design Patterns. A Composite is a tree structure where each node can be represented as a common type so that they can be acted on in a consistent manner, regardless of their concrete implementations. This allows a consumer of the composite to avoid the complexity of having to distinguish the objects individually. I believe that the posts for PTOM are going to centered around a coffee shop, but I’m having a difficult time thinking of a good composite example around that, so let’s say I’m building coffee machines instead.

Lets pretend that I have a model of a coffee machine and all of its parts. Many of the parts are logical and are made up of only child parts. I might have a class that looks something like the following (simplistically implemented for example purposes):

public class CoffeeMachine
{
public FluxCapacitor FluxCapacitor { get; set; }
public PowerCord PowerCord { get; set; }
}
 
Lets say FluxCapacitor is a composite part made up of child parts and those child parts may have their own child parts. I want to build a class that allows me to output all the given parts for a machine, but I don’t want my part list generator to know about each specific details of the machine or any of its specific parts. If I did couple the list generator to those implementations, that would force me to modify the list generator every time we added or changed a part, which would be a violation of OCP. We can solve this problem by creating an interface that lets us traverse our structure as a composite.
 
public interface IPart
{
IEnumerable<IPart> GetChildParts();
}

You will notice that the IPart returns child instances of other IParts. This gives us a recursive structure and lets us act on that structure as a composite. By implementing this interface on all of our parts, when can then walk the hierarchy and perform operations without knowing the details of each implementation. We can implement this on our CoffeeMachine and our other parts like the following:

public class CoffeeMachine : IPart
{
public FluxCapacitor FluxCapacitor { get; set; }
public PowerCord PowerCord { get; set; }

public IEnumerable<IPart> GetChildParts()
{
yield return FluxCapacitor;
yield return PowerCord;
}
}

public class FluxCapacitor : IPart
{
public Switch Switch { get; set; }
public PowerBooster PowerBooster { get; set; }

public IEnumerable<IPart> GetChildParts()
{
yield return Switch;
yield return PowerBooster;
}
}

public class PowerCord : IPart
{
public IEnumerable<IPart> GetChildParts()
{
yield break;
}
}

Now we can easily build a part list generator that uses a recursive method to walk through the hierarchy and outputs the names of the parts.

public class PartListGenerator
{
public void OutpAllParts(IPart part, TextWriter textWriter)
{
OuputAllPartsByLevel(part, textWriter, 0);
}

private static void OuputAllPartsByLevel(IPart part, TextWriter textWriter, int level)
{
textWriter.WriteLine("{0}{1}", new string('t', level), part.GetType().Name);

foreach (var childPart in part.GetChildParts())
{
OuputAllPartsByLevel(childPart, textWriter, level + 1);
}
}
}

By running our CoffeeMachine through this class, we end up with something similar to the following output:

CoffeeMachine
    FluxCapacitor
        Switch
        PowerBooster
    PowerCord

 

You may have noticed that we have the GetChildParts() method implemented on the leaf parts as well (see PowerCord). This may be a slight violation of ISP, but it is side effect free and it does not violate LSP therefor it does not bother me too much. In the real world, IPart would probably have other functionality and you could split out the GetChildParts() into a separate interface such as ICompositePart which would only be implemented by non leafs. This would require the PartListGenerator to know about IParts and ICompositeParts which may or may not be desirable. It’s really a trade off here as to what makes the most sense for your needs.
 
There are several variations of the Composite Design Pattern and my example can probably be considered one of them. The Composite can also be combined with other patterns such as the Specification or Command patterns. You can find lots of other examples on the web and in Design Patterns : Elements of Reusable Object-Oriented Software. I recommend picking up that book for more information on the Composite and many other must know patterns.
 
Pablo’s Topic of the Month – November: Design Patterns