Composite
The composite pattern
The composite pattern is a structural pattern and is a way of creating similar objects that form a tree like structure.It's difficult to find a reason for this pattern but imagine a simplified construction of the graphics on this page.
We have pictures, text, lines, rectangles etc, well the pictures could contain text, lines and rectangles, also, the pictures could contain pictures and the rectangles would almost definitely contain lines.
The object structure would be similar to:
It's clear that we need some sort of tree structure, this leads to the inheritance graph below.
Now, the structure I've created is a little different to the one in Design Patterns [GOF] and the reason is to prevent stamp coupling.
Click to expand to see the changes between GOF and my design
The GOF design has the abstract class Component with the methods:
- Operation()
- Add(Component)
- Remove(Component)
- GetChild(int) (?)
So after some fancy pictures, we go into an example, with the creation of the Component abstraction:
abstract class Component {
private string name;
private int layer;
public Component(string name) {
this.name = name;
}
public virtual void Operation(int layer) {
this.layer = layer;
OperationCommonCodeTemplate();
}
protected virtual void OperationCommonCodeTemplate() {
Console.WriteLine(new String('-', layer) + name);
}
}
Things to notice with the code are the int layer member variable
which just adds a little flair as it adds 'dashes' recursively before showing the name of the
Component and the OperationCommonCodeTemplate
method this is part of the
Template Method Pattern
and I'm using it to shore-up some common code that can be overridden.private string name;
private int layer;
public Component(string name) {
this.name = name;
}
public virtual void Operation(int layer) {
this.layer = layer;
OperationCommonCodeTemplate();
}
protected virtual void OperationCommonCodeTemplate() {
Console.WriteLine(new String('-', layer) + name);
}
}
The implementation of a leaf of the tree is very simple in my example and is:
class Leaf : Component {
public Leaf(string name) : base(name) { }
}
public Leaf(string name) : base(name) { }
}
The implementation of the composite part, which is a little more interesting, is:
class Composite : Component {
private List components = new List();
public Composite(string name) : base(name) { }
public override void Operation(int layer) {
base.Operation(layer);
foreach (Component component in components) {
component.Operation(layer + 1);
}
}
public void Add(Component component) {
components.Add(component);
}
public void Remove(Component component) {
components.Remove(component);
}
}
private List
public Composite(string name) : base(name) { }
public override void Operation(int layer) {
base.Operation(layer);
foreach (Component component in components) {
component.Operation(layer + 1);
}
}
public void Add(Component component) {
components.Add(component);
}
public void Remove(Component component) {
components.Remove(component);
}
}
Here's some test code to test the implementation:
static void Main(string[] args) {
Component leafA = new Leaf("LeafA");
Component leafB = new Leaf("leafB");
Component leafC = new Leaf("leafC");
Composite compositeA = new Composite("compositeA");
Component leafD = new Leaf("leafD");
Component leafE = new Leaf("leafE");
compositeA.Add(leafA);
compositeA.Add(leafB);
compositeA.Add(leafC);
Composite root = new Composite("root");
root.Add(compositeA);
root.Add(leafD);
root.Add(leafE);
root.Operation(0);
Console.ReadKey();
}
And the output:
Component leafA = new Leaf("LeafA");
Component leafB = new Leaf("leafB");
Component leafC = new Leaf("leafC");
Composite compositeA = new Composite("compositeA");
Component leafD = new Leaf("leafD");
Component leafE = new Leaf("leafE");
compositeA.Add(leafA);
compositeA.Add(leafB);
compositeA.Add(leafC);
Composite root = new Composite("root");
root.Add(compositeA);
root.Add(leafD);
root.Add(leafE);
root.Operation(0);
Console.ReadKey();
}
root
-compositeA
--LeafA
--leafB
--leafC
-leafD
-leafE
-compositeA
--LeafA
--leafB
--leafC
-leafD
-leafE
Summary
That's the composite pattern. I've changed the implementation compared to
the GOF book, but the late John Vlissades in his excellent follow up book,
Pattern Hatching,
makes a valid argument that you don't know if the leaf in the future will need the
same functionality as the composite so there's always a trade off, and
the developer needs to understand the problem to be solved and use his/her strategic judgement.