The composite design pattern is used to “compose” objects into tree structure type hierarchies. Clients are then able to uniformly view objects and compositions. These hierarchies are comprised of two types of objects called “composites” and “leafs”. Composites are tree structures that can contain other composites or leafs. Leafs are “single objects” that cannot contain children (composites). Composites and leafs implement (extend) the same Component
abstract class making it easier for clients to traverse the collections.
Example Implementation
This example uses classes named Parent
and Child
. Both of these classes implement (extend) the FamilyComponent
abstract class. Parent
represents a composite node while Child
represents a leaf node. A parent can have many children. A parent can also have children who are also parents; and so on. This example doesn’t take into consideration that these objects may change over time (for example when a child becomes a parent).
FamilyComponent abstract class
FamilyComponent
contains several methods. In this example the method requestPermission()
is specific to Child
and the grantPermission()
method is specific to Parent
. The default implementation of these methods is UnsopportedOperationException
because if one of the implementations doesn’t support an operation method (like requestPermission()
or grantPermission()
) they just inherit the default implementation.
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 | public class FamilyComponent { // "Composite" Methods public void add(FamilyComponent familyComponent) { throw new UnsupportedOperationException(); } public void remove(FamilyComponent familyComponent) { throw new UnsupportedOperationException(); } public FamilyComponent getChild(int i) { throw new UnsupportedOperationException(); } // Operation methods public String getName() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } // Children need to ask for permission before performing some tasks public void requestPermission() { throw new UnsupportedOperationException(); } // Parents need to grant permission to children when they request it public boolean grantPermission() { throw new UnsupportedOperationException(); } } |
The Child Implementation
Here’s the child implementation, it’s quite straightforward. The operation method print()
prints the name of the child. Since this is a leaf node it doesn’t need to override any of the “composite” methods from FamilyComponent
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Child extends FamilyComponent { String name; public Child(String name) { this.name = name; } @Override public String getName() { return name; } @Override public void requestPermission() { // ask for permission } @Override public void print() { System.out.println(String.format("Name: %s", name)); } } |
The Parent Implementation
The Parent
implementation has a bit more to it. Since it can have children or parents (any implementation of FamilyComponent
) it must implement all of the “composite” methods from the abstract FamilyComponent
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class Parent extends FamilyComponent { ArrayList<FamilyComponent> familyComponents = new ArrayList<>(); String name; public Parent(String name) { this.name = name; } @Override public void add(FamilyComponent familyComponent) { familyComponent.add(familyComponent); } @Override public void remove(FamilyComponent familyComponent) { familyComponent.remove(familyComponent); } @Override public FamilyComponent getChild(int i) { return familyComponents.get(i); } @Override public String getName() { return name; } @Override public boolean grantPermission() { return false; // No, you can't do that. } @Override public void print() { System.out.println(String.format("Name: %s", name)); // What's the best way to loop over all the possible children/parents here for this object? } } |
The Iterator Pattern
Notice the comment “What’s the best way to loop over all the possible children/parents here for this object?”. We could just loop over the collection familyComponents
and invoke print()
but we won’t see print()
invoked on any of the “compsite” objects. My last design pattern post was implementing the iterator pattern in java. The composite and iterator patterns work well together. Let’s use the iterator pattern to implement the print()
method for Parent
. First let’s add a new method to the FamilyComponent
abstract class which will be responsible for creating the iterator.
1 2 3 | public Iterator<FamilyComponent> createIterator() { throw new UnsupportedOperationException(); }; |
The Parent
implementation of createIterator()
:
1 2 3 4 5 6 7 8 | @Override public Iterator<FamilyComponent> createIterator() { if (iterator == null) { iterator = new FamilyCompositeIterator(familyComponents.iterator()); } return iterator; } |
FamilyCompositeIterator
The FamilyCompositeIterator
is a new class which can handle iterating over any composite. The iterator peek()
s at the top of the stack and determines if it should have a look inside or pop()
it from the stack and move on.
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 | public class FamilyCompositeIterator implements Iterator { final Stack<Iterator<FamilyComponent>> stack = new Stack<>(); public FamilyCompositeIterator(Iterator iterator) { stack.push(iterator); } @Override public boolean hasNext() { if (stack.isEmpty()) { return false; } else { Iterator<FamilyComponent> iterator = stack.peek(); if (!iterator.hasNext()) { stack.pop(); return hasNext(); } else { return true; } } } @Override public Object next() { if (hasNext()) { Iterator<FamilyComponent> iterator = stack.peek(); FamilyComponent familyComponent = iterator.next(); stack.push(familyComponent.createIterator()); return familyComponent; } else { return null; } } } |
The Child
implementation of createIterator()
:
1 2 3 4 | @Override public Iterator<FamilyComponent> createIterator() { return new NullIterator(); } |
NullIterator
The NullIterator
class does the opposite of FamilyCompositeIterator
. It just indicates that there’s “nothing to see here, move along”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class NullIterator implements Iterator { @Override public boolean hasNext() { return false; } @Override public Object next() { return null; } @Override public void remove() { throw new UnsupportedOperationException(); } } |
The Parent print() method implementation
Now that we have implementations of createIterator()
, FamilyCompositeIterator
and NullIterator
we can finish the print()
implementation of the Parent
class.
1 2 3 4 5 6 7 8 9 10 11 12 | @Override public void print() { System.out.println(String.format("Name: %s", name)); FamilyCompositeIterator iterator = new FamilyCompositeIterator(familyComponents.iterator()); while (iterator.hasNext()) { FamilyComponent component = (FamilyComponent) iterator.next(); component.print(); } } |
Summary
Clients can now invoke a single method to traverse over these objects without knowing the internal details. The composite pattern is great for managing and traversing hierarchies of data such as in this example. The composite pattern allows individual objects and compositions of objects to be treated uniformly.
Open questions / concerns
I’m not completely sure about Parent
s implementation of the print()
method, more specifically if I should be casting FamilyComponent
from Object
. I also left out the remove()
method in FamilyCompositeIterator()
. I’m not exactly sure how the implementation should look. I’m also seeing some unchecked assignment warnings in my IDE that I’m not sure about.