Design patterns episode 9 – Composite
With unexpected delay but patterns are back! In this episode – composite.
The composite is generally used to model tree-like structures in a way that classes that are using the composite can handle its elements in one particular way without having to distinguish whether an element has children – a composite, or hasn’t – a leaf (in other words, whether we can go further down the tree or element is a dead-end).
The first thing that comes up when thinking about that kind of structure would be a directory tree where folders are composites and files are leafs. Or any other structure alike: corporate structure (managers and subordinates), online shops (categories and concrete items) and many many more.
DEFINITION
Let’s see how the Head First book defines this pattern:
The Composite Pattern allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets client treat individual objects and composition of objects uniformly.
(Freeman, Eric, et al. Head First Design Patterns. O’Reilly Media, Inc. 2004)
So, in other words we have tree structure of objects and collection of objects and the client doesn’t need to care about if it’s a leaf or a composite. This is the key feature of this pattern. For instance, let’s think about file system where we have files and directories. Both of them are elements of file system and should be treated the same: the client can perform the same operations on both – rename, delete, get size etc. But obviously we can do more things with directories (as this is a collection of files and directories), therefore composite pattern isn’t applicable here (of course you could use typecasting to make it work but that’s a different thing).
The diagram shows what discussed above – both concretions are treated the same, nothing different can client do with one or the other type. The diagram presented here is different than what’s in the book. The book adds further methods: add(), remove(), getChild(). But isn’t that inconsistent with the definition? How can you add a further child to a leaf? You could throw an exception but wouldn’t that violate the Interface Segregation Principle? That kind of issues have been greatly discussed here by the guy who I quoted on multiple occasions.
So, to conclude this aspect of the pattern – my opinion is that we should stick to ISP and not force any class to implement something that the class doesn’t use. Period. Let’s move on to code example.
EXAMPLE
In our code example we’re going to build a store’s menu. Let’s start with the abstraction:
1 2 3 4 5 |
public interface IStoreElement { string Name { get; set; } string ListOut(int indent = 0); } |
Our IStoreElement consist of name property and a method to list self in the menu: there will be different implementations of this method for leaves and composites. This method can take optional argument as we want to nicely format the menu by indentation of sub-components.
Now the leaf class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class StoreItem : IStoreElement { public string Name { get; set; } public StoreItem(string _name) { Name = _name; } public string ListOut(int indent = 0) { string result = ""; for (int i = indent; i > 0; i--) { result += "-"; } result += String.Format("Item: {0}", Name); return result; } } |
Leaf’s implementation of ListOut() method works that it returns a string with given number of dashes and its name.
And finally composite class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class StoreCategory : IStoreElement { public string Name { get; set; } private IList<IStoreElement> members; public StoreCategory(string _name, IList<IStoreElement> _members) { Name = _name; members = _members; } public string ListOut(int indent = 0) { string result = ""; for (int i = indent; i > 0; i--) { result += "-"; } result += String.Format("Category: {0}", Name); foreach (IStoreElement element in members) { result += String.Format("\n{0}", element.ListOut(indent + 1)); } return result; } } |
This class is equipped with private list of objects that implement our base interface. This list can be set in constructor.
The ListOut() method starts with the same thing that counterparting method in leaves but before returning the value recursively invokes the same method for all IStoreElements that the composite holds in its private list. This invocation takes indent parameter increased by 1. This way we achieve that nice formatting of the list once requested in the topmost component.
Now, let’s see how it work all together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
static void Main(string[] args) { IStoreElement myStore = generate(); Console.WriteLine(myStore.ListOut()); } static IStoreElement generate() { IStoreElement subcat1 = new StoreCategory("Foods", new List<IStoreElement> { new StoreItem("Bread"), new StoreItem("Veggies"), new StoreItem("Cheese"), new StoreCategory("Drinks", new List<IStoreElement> { new StoreItem("Juice"), new StoreItem("Mineral water"), }) }); IStoreElement subcat2 = new StoreCategory("Chems", new List<IStoreElement> { new StoreItem("Toothpaste"), new StoreItem("Shampoo"), new StoreItem("Soap") }); IStoreElement item1 = new StoreItem("Painkillers"); IStoreElement item2 = new StoreItem("Lottery ticket"); IStoreElement result = new StoreCategory("Local shop", new List<IStoreElement> { subcat1, subcat2, item1, item2 }); return result; } |
We generate the list and then print to the console the string returned by ListOut().
To sum things up: he Composite Pattern is used to represent tree-like structures where all their elements can be treated uniformly, where single components are called leaves and collections of components are called composites. The pattern can be equipped with methods to modify the content but this lead to violation of Interface Segregation Principle.
Next week: the State Pattern. I hope that this time without any delay. Check the links and source code below. Thank you!
Composite pattern by Christopher Okhravi: link [YouTube]
Composite vs. Decorator by Christopher Okhravi: link [YouTube]
Composite pattern by Michael: link [YouTube]
Source code: TXT file