The GoF says you use the Composite
design pattern to “Compose objects into tree structures to represent part-whole
hierarchies. Composite lets clients treat individual objects and compositions
of objects uniformly.”
Motivation
When dealing with Tree-structured
data, programmers often have to discriminate between a leaf-node and a branch.
This makes code more complex, and therefore, error prone. The solution is an
interface that allows treating complex and primitive objects uniformly.
The composite pattern describes that a group of objects are to be treated in the
same way as a single instance of an object. The intent of a composite is to
"compose" objects into tree structures to represent part-whole hierarchies
What is Part-whole hierarchy?
A system consists of subsystems or
components. Components can further be divided into smaller components. Further
smaller components can be divided into smaller elements. This is a part-whole
hierarchy.
Composite pattern is used where we need to treat a
group of objects in similar way as a single object. Composite pattern composes
objects in term of a tree structure to represent part as well as whole hierarchy.
This type of design pattern comes under structural pattern as this pattern
creates a tree structure of group of objects.
To
implement the Composite pattern, the GoF suggests that you use an abstract
class as the basis for both the leaves and branches in the tree. Doing so gives
the leaves and branches a common set of methods, which is what the Composite
pattern is all about.
Example 1
In a small organization,there are 5 employees.At top position,there is 1 general manager. Under general manager,there are two employees, one is manager and other is developer and further manager has two developers working under him. We want to print name and salary of all employees from top to bottom.
Comparing
from above generic elements. Our example consist of following elements.
- Manager(Composite)
- Developer(Leaf)
- Employee(Component)
Employee.java(Component)
public interface Employee {
public void add(Employee employee);
public void remove(Employee employee);
public Employee getChild(int i);
public String getName();
public double getSalary();
public void print();
}
Manager.java(Composite)
public class Manager implements Employee {
private String name; private double salary;
List<Employee> employees = new ArrayList<Employee>();
public Manager(String n, double d) {
name = n; salary = d;
}
@Override
public void add(Employee employee) {
employees.add(employee);
}
@Override
public Employee getChild(int i) {
return employees.get(i);
}
@Override
public String getName() { return name; }
@Override
public double getSalary() { return salary; }
@Override
public void print() {
System.out.println("Manager Name =" + getName());
System.out.println("Manager Salary =" + getSalary());
Iterator<Employee> employeeIterator = employees.iterator();
while (employeeIterator.hasNext()) {
Employee employee = employeeIterator.next();
employee.print();
}
}
@Override
public void remove(Employee employee) {
employees.remove(employee);
}}
Developer.java(Leaf)
/**
* In this class,there are many methods which are not applicable to developer because * it is a leaf node. */
1: public class Developer implements Employee {
2: private String name; private double salary;
3: public Developer(String name,double salary) {
4: this.name = name;
5: this.salary = salary;
6: }
7: @Override
8: public void add(Employee employee) {
9: //this is leaf node so this method is not applicable to this class.
10: }
11: @Override
12: public Employee getChild(int i) {
13: return null;
14: }
15: @Override
16: public String getName() {
17: return name;
18: }
19: @Override
20: public double getSalary() {
21: return salary;
22: }
23: @Override
24: public void print() {
25: System.out.println("-------------");
26: System.out.println("Dev Name ="+getName()+"Dev Salary ="+getSalary());
27: System.out.println("-------------");
28: }
29: @Override
30: public void remove(Employee employee) {
31: //this is leaf node so this method is not applicable to this class.
32: }
33: }
1: public class CompositeDesignPatternMain {
2: public static void main(String[] args) {
3: Employee empManager = new Manager("Sam",25000);
4: Employee emp1 = new Developer("Gubbo", 10000);
5: Employee emp2 = new Developer("TG", 15000);
6: empManager.add(emp1);
7: empManager.add(emp2);
8: Employee emp3=new Developer("AP", 20000);
9: //Manager generalManager=new Manager("JA", 50000); NOT This
10: Employee empGM = new Manager("JA", 50000);
11: empGM.add(emp3);
12: empGM.add(empManager);
13: empGM.print();
14: } }
Output of the
program
Manager Name =JA
Manager Salary =50000.0
-------------
Dev Name =AP
Dev Salary =20000.0
-------------
Manager Name =Sam
Manager Salary =25000.0
-------------
Dev Name =Gubbo
Dev Salary =10000.0
-------------
-------------
Dev Name =TG
Dev Salary =15000.0
-------------
As seen in the output, the composite pattern allows us to
perform operations on the composites and leaves that make up a tree structure
via a common interface.
Example 2 :
1: public interface Group {
2: public void assemble();
3: public void addGroup(Group g);
4: public void removeGroup(Group g);
5: }
1: public class Block implements Group {
2: @Override
3: public void assemble() {
4: System.out.println(" Assemble the Block ");
5: }
6: @Override
7: public void addGroup(Group g) {
8: // No need to implement Group functionality as LEAF NODE
9: }
10: @Override
11: public void removeGroup(Group g) {
12: // No need to implement Group functionality as LEAF NODE
13: }
14: }
1: public class Structure implements Group {
2: private List<Group> groups = new ArrayList<Group>();
3: public void addGroup(Group group){
4: groups.add(group);
5: }
6: public void removeGroup(Group group){
7: groups.remove(group);
8: }
9: @Override
10: public void assemble() {
11: for(Group gp : groups){
12: gp.assemble();
13: }
14: }
15: }
1: public class CompositeTest {
2: public static void main(String[] args) {
3: Block b1 = new Block();
4: Block b2 = new Block();
5: Block b3 = new Block();
6: Structure structure1 = new Structure();
7: Structure structure3 = new Structure();
8: Structure structure2 = new Structure();
9: structure1.addGroup(b1);
10: structure1.addGroup(b2);
11: structure2.addGroup(b3);
12: structure3.addGroup(structure1);
13: structure3.addGroup(structure2);
14: structure3.assemble();
15: }
16: }
Output of the program
Assemble the Block
Assemble the Block
Assemble the Block
Notes
:
- Importance of composite pattern is, the group of objects should be treated similarly as a single object.
- Manipulating a single object should be as similar to manipulating a group of objects. In sync with our example, we join primitive blocks to create structures and similarly join structures to create house.
- Recursive formation and tree structure for composite should be noted.
- Clients access the whole hierarchy through the components and they are not aware about if they are dealing with leaf or composites.
Advantages of Composite pattern:
- Compose more than one similar objects so that they can be manipulated as one object.
- Flexibility of structure and manageable interface.
- Structure can be changed anytime by calling the respective methods (add or remove) on a composite.
Java Usage / Java Real time
- java.awt.Container#add(Component) – in awt, we have containers and components – a classic implementation
- javax.faces.component.UIComponent#getChildren()
- File System Implementation
- The Apache Struts framework includes a JSP tag library, known as Tiles, that lets you compose a Webpage from multiple JSPs. Tiles is actually an implementation of the J2EE (Java 2 Platform, Enterprise Edition) CompositeView pattern, itself based on the Design Patterns Composite pattern.
Related
Patterns
Decorator Pattern - Decorator is often used with Composite. When decorators
and composites are used together, they will usually have a common parent class.
So decorators will have to support the Component interface with operations like
Add, Remove, and GetChild.
No comments:
Post a Comment