The Composite Pattern Revisited

The composite pattern has been dismissed as being tricky and slightly special case.  Like the visitor pattern, we tend to associate it with dealing with trees* and such relatively obscure topics as semantic evaluation. I’m going to argue that in fact the opposite is the case: it’s a really easy to use thing that you probably see all the time and don’t think about too much.

Actually, it’s closest to the adapter pattern.  But while the adapter pattern is about jamming a square peg into a round hole, the composite pattern is about slipping two round pegs into the same round hole.  Of course, the physical realization of this would be impossible, but code’s a lot more flexible than IKEA furniture.

So, how do we achieve this?  Well, imagine a simple interface like this

public interface ILogger {
    void WriteLine(string line);
}

and two implementations

public class ConsoleLogger : ILogger {
    public void WriteLine(string line) {
        Console.WriteLine(line);
    }
}

// Yeah, the implementation of FileLogger is awful
// you don't need to tell me
public class FileLogger : ILogger {
    string fileName;

    public FileLogger(string fileName) {
        this.fileName = fileName;
    }

    public void WriteLine(string line) {
        using (var writer = new StreamWriter(fileName, true)) {
            writer.WriteLine(line);
            writer.Flush();
        }
    }
}

So, imagine we now want to write to the console and to the file.  Or to two different files.  Do we need to write a new logger each of these cases?  No, we just need the one, the Composite logger.

public class CompositeLogger : ILogger {
    IEnumerable<ILogger> underlying;

    public CompositeLogger(IEnumerable<ILogger> underlying) {
        this.underlying = underlying;
    }

    public void WriteLine(string line) {
        foreach (var logger in underlying) {
            logger.WriteLine(line);
        }
    }
}

This class gives us all the aggregation we need and allows us to easily implement the single responsibility principle.  A class taking one logger can now write to two loggers without changing the code.  Incidentally, if you look at the Solution Transform source code, you’ll see that I have two composite classes that work exactly as defined above.

Aggregation of Results

That’s the basic idea.  However, my example rather deliberately skirted an important detail: what happens if your interface returns values?**  The answer is, it depends.  We’re starting to move away from the classical definition Let’s take an example from the dotcom world.  Imagine that you were running a shopfront application.  You’ve got several suppliers with price providers.  But you only want the one price.  Well, the sensible thing to do would be to quote the lowest price.  So, on top of IPriceProvider you could have MinimumPriceProvider as a composite implementation. In fact, most aggregate functions could be used in various contexts:

  • Minimum
  • Maximum
  • Sum
  • Product
  • First (or First not null)
  • And (True if all of the underlying implementations return true)***
  • Or (True if one of the underlying implementations return true)

The price example is oversimplified, however.  Usually, you don’t only want to know the lowest price, you want to know who is quoting the lowest price.  The composite pattern doesn’t really apply here.  Actually, we’re probably talking about chain of responsibility again.  Composite objects are dumb.  If you’re talking logic and decision making, composite isn’t the right design.

Composites in Use

There’s a couple of composite implementations that you never even think about.  Any logging system is an obvious example, but MulticastDelegates are composites as well: they expose the method signature and call multiple implementations when invoked.  It’s also a fundamental building block of any publish and subscribe system.  Jeremy Miller’s got a list of common composites but, inevitably, most of them are tree structures.  Obviously, you start putting composites in other composites and you get a tree structure, but there are plenty of flatter problems that find composites useful.

So, when should you use it?  Basically. anytime you process a list of objects, revisit whether your code would be simpler if you just dealt with one object, and used a composite to for aggregation/distribution duties.

*Please bear in mind that I’m referring to that JavaWorld article as an example of someone making a mistake.  I don’t really want to dissect exactly what’s wrong with the rest of the article, but I’m not recommending it.

**In fact, I don’t think you can return values according to the classical definition.  It’s hard to find an example on the internet that does.  I’m sure someone with a copy of Gamma et al handy will correct me.

***Take a look at the Rhino Mocks constraints code for a good example of this.

Technorati Tags: ,

Published by

Julian Birch

Full time dad, does a bit of coding on the side.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s