The Composite Design Pattern
The Composite Pattern is designed to build a class hierarchy from primitive and composite objects, that may represent complex structures, but have similar behavior.
What problems does it solve?
The pattern is used to implement a tree-like, hierarchical structure of elements, that have same behavior. The elements can be primitive (leafs) or composite objects. Composite objects have children, which might be leafs or composite objects.
Glossary:
- Composition - the abstraction of the composites and the leafs, their common functionality
- Leaf - primitive objects in the composition - they implement the composition, but doesn’t have children
- Composite - composite objects in the composition - they implement the child behavior, bu also have children of type Composition
- Client - The client operates with the compositions in a general manner. In fact the client doesn’t even know if it’s a leaf or composite.
Pros:
- Client knows only about the general behavior off all components and treats them the same way.
- to decouple client
- to achieve good level of abstraction
Cons:
- It is hard to restrict the components of the composite
How to recognize it?
When you have behavioral methods, that take an instance of same abstract/interface type into and create or process a tree structure.
Folder homeDir = new Folder("Home");
Folder documentsDir = new Folder("Personal Documents");
homeDir.addChild(documentsDir);
documentsDir.addChild(new File("CV.doc"));
Examples from Java API
java.awt.Container#add(Component) (practically all over Swing thus)
javax.faces.component.UIComponent#getChildren() (practically all over JSF UI thus)
Scenarios
-
Use it for a tree-like structure, when all elements have some general behavior
-
Like a FileSystem, where you have tree of folders and files and you can move them, delete the etc (general behavior).
Example 1
We have hierarchical folder structure, that may contain other folders in files.
1). Entry Interface (our Composition)
public interface Entry {
String getName();
void setName(String name);
void ls(boolean recursive);
}
2). File Class (our Leaf or Primitive object)
public class File implements Entry {
@Getter
@Setter
private String name;
public File(String name) {
this.name = name;
}
@Override
public void ls(boolean recursive) {
System.out.println(getName());
}
}
3). Folder Class (our Composite object, that extends the primitive behavior, but also has children)
public class Folder extends File {
private List<Entry> children = new ArrayList<>();
public Folder(String name) {
super(name);
}
public void addChild(Entry child) {
this.children.add(child);
child.setName(getName() + "\\" + child.getName());
}
@Override
public void ls(boolean recursive) {
System.out.println(getName());
for (Entry c : this.children) {
if(recursive){
c.ls(true);
}else{
System.out.println(c.getName());
}
}
}
}
4). FileSystemClient class (our client)
public class FSClient {
public static void ls(Entry entry){
entry.ls(false);
}
public static void lsDeep(Entry entry){
entry.ls(true);
}
}
- Main class
public class _Main { public static void main(String[] args) { Folder homeDir = new Folder("Home"); Folder documentsDir = new Folder("Personal Documents"); homeDir.addChild(documentsDir); documentsDir.addChild(new File("CV.doc")); documentsDir.addChild(new File("Diplom.pdf")); documentsDir.addChild(new File("ID_Photo.png")); Folder examsDir = new Folder("Exams"); documentsDir.addChild(examsDir); Folder examAEDir = new Folder("AE"); examsDir.addChild(examAEDir); examAEDir.addChild(new File("AE_Book.pdf")); examAEDir.addChild(new File("AE_Sample_Tests.pdf")); Folder examDEDir = new Folder("DE"); examsDir.addChild(examDEDir); examDEDir.addChild(new File("DE_Book.pdf")); examDEDir.addChild(new File("DE_Sample_Tests.pdf")); FSClient.lsDeep(homeDir); } }
Output:
Home Home\Personal Documents Home\Personal Documents\CV.doc Home\Personal Documents\Diplom.pdf Home\Personal Documents\ID_Photo.png Home\Personal Documents\Exams Home\Personal Documents\Exams\AE Home\Personal Documents\Exams\AE\AE_Book.pdf Home\Personal Documents\Exams\AE\AE_Sample_Tests.pdf Home\Personal Documents\Exams\DE Home\Personal Documents\Exams\DE\DE_Book.pdf Home\Personal Documents\Exams\DE\DE_Sample_Tests.pdf