COMPOSITE
Design Pattern Space Purpose ScopeCreationalStructuralBehavioral ClassFactory MethodAdapterInterpreter Template Method ObjectAbstract factory Builder Prototype Singleton Adapter Bridge Composite Facade Flyweight Proxy Chain of Responsibility Command Iterator Mediator Memento State Strategy Visitor Observer
Intent Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. This is called recursive composition.
Structure
Participants Component declares the interface for object composition implements default behaviour declares an interface for accessing and managing the child components Leaf represents leaf objects in the composition Composite defines behaviour for components having children stores child components implements child-related operations in the Component interface Client manipulates objects in the composition through the Composite interface
Consequences Benefits It makes it easy to add new kinds of components It makes clients simpler, since they do not have to know if they are dealing with a leaf or a composite component
Example import java.util.*; interface Component { public String defaultMethod(); public ArrayList getChildren(); public boolean addComponent(Component c); public boolean removeComponent(Component c); }
class Composite implements Component { private String id; private ArrayList components = new ArrayList (); public Composite(String identification) { id = identification; } public String defaultMethod() { String s = "(" + id + ":"; for (Component child : getChildren()) s = s + " " + child.defaultMethod(); return s + ")"; } public ArrayList getChildren() { return components; } public boolean addComponent(Component c) { return components.add(c); } public boolean removeComponent(Component c) { return components.remove(c); }
class Leaf implements Component { private String id; public Leaf(String identification) { id = identification; } public String defaultMethod() { return id; } public ArrayList getChildren() { return null; } public boolean addComponent(Component c) { return false; } public boolean removeComponent(Component c) { return false; } }
class CompositePattern { public static void main(String[] args) { Composite england = new Composite("England"); Leaf york = new Leaf("York"); Leaf london = new Leaf("London"); england.addComponent(york); england.addComponent(london); england.removeComponent(york); Composite france = new Composite("France"); france.addComponent(new Leaf("Paris")); Composite europe = new Composite("Europe"); europe.addComponent(england); europe.addComponent(france); System.out.println( europe.defaultMethod() ); }
Composite Example - Motivation GUI Windows and GUI elements How does the window hold and deal with the different items it has to manage? Widgets are different than WidgetContainers
Bad News Implementation class Window { Buttons[] myButtons; Menus[] myMenus; TextAreas[] myTextAreas; WidgetContainer[] myContainers; public void update() { if ( myButtons != null ) for ( int k = 0; k < myButtons.length(); k++ ) myButtons[k].refresh(); if ( myMenus != null ) for ( int k = 0; k < myMenus.length(); k++ ) myMenus[k].display(); if ( myTextAreas != null ) for ( int k = 0; k < myButtons.length(); k++ ) myTextAreas[k].refresh();
Bad News Implementation if ( myContainers != null ) for ( int k = 0; k < myContainers.length(); k++ ) myContainers[k].updateElements(); etc. } public void fooOperation() { if ( blah ) etc. if ( blah ) etc. }
The Composite Pattern Component implements default behavior for widgets when possible Button, Menu, etc overrides Component methods when needed WidgetContainer will have to overrides all widgetOperations
Implementation Issues A composite object knows its contained components, that is, its children. Should components maintain a reference to their parent component? Depends on application, but having these references supports the Chain of Responsibility pattern
Explicit Parent References Aid in traversing the structure class WidgetContainer { Component[] myComponents; public void update() { if ( myComponents != null ) for ( int k = 0; k < myComponents.length(); k++ ) myComponents[k].update(); } public add( Component aComponent ) { myComponents.append( aComponent ); aComponent.setParent( this ); }
Explicit Parent References class Button extends Component { private Component parent; public void setParent( Component myParent) { parent = myParent; } etc. }
Implementation Issues Where should the child management methods (add(), remove(), getChild()) be declared? In the Component class: Gives transparency, since all components can be treated the same. But it's not safe, since clients can try to do meaningless things to leaf components at run-time. In the Composite class: Gives safety, since any attempt to perform a child operation on a leaf component will be caught at compile-time. But we lose transparency, since now leaf and composite components have different interfaces.
Transparent vs. Safe
Applicability Use Composite pattern when You want to represent part-whole hierarchies of objects You want clients to be able to ignore the difference between compositions of objects and individual objects
Java Use of Composite - AWT Widgets
Another Example
Problem Each of the company members receives a salary, and we could at any time ask for the cost of any employee to the company. We define the cost as the salary of that person and those of all his subordinates. Solution Here is an ideal example for a composite: The cost of an individual employee is simply his salary (and benefits). The cost of an employee who heads a department is his salary plus those of all his subordinates.
We would like a single interface that will produce the salary totals correctly whether the employee has subordinates or not. public float getSalaries(); At this point, we realize that the idea of all Composites having the same standard interface is probably naïve. We’d prefer that the public methods be related to the kind of class we are actually developing. So rather than have generic methods like getValue(), we’ll use getSalaries().
The Employee Class public class Employee { //Our Employee class will store the name and salary of each employee, and String name; // allow us to fetch them as needed. float salary; Vector subordinates; // public Employee(String _name, float _salary) { name = _name; salary = _salary; subordinates = new Vector(); } // public float getSalary() { return salary; } // public String getName() { return name; }
public void add(Employee e) { subordinates.addElement(e); } // public void remove(Employee e) { subordinates.removeElement(e); } public Enumeration elements() { return subordinates.elements(); }
public float getSalaries() { float sum = salary; //this one’s salary //add in subordinates salaries for(int i = 0; i < subordinates.size(); i++) { sum += ((Employee)subordinates.elementAt(i)).getSalaries(); return sum; }
Building the Employee Tree boss = new Employee("CEO", ); boss.add(marketVP = new Employee("Marketing VP", )); boss.add(prodVP = new Employee("Production VP", )); marketVP.add(salesMgr = new Employee("Sales Mgr", 50000)); marketVP.add(advMgr = new Employee("Advt Mgr", 50000)); //add salesmen reporting to Sales Manager for (int i=0; i<5; i++) salesMgr.add(new Employee("Sales "+ new Integer(i).toString(), F +(float)(Math.random()-0.5)*10000)); advMgr.add(new Employee("Secy", 20000));
Building the Employee Tree prodVP.add(prodMgr = new Employee("Prod Mgr", 40000)); prodVP.add(shipMgr = new Employee("Ship Mgr", 35000)); //add manufacturing staff for (int i = 0; i < 4; i++) prodMgr.add( new Employee("Manuf "+ new Integer(i).toString(), F +(float)(Math.random()-0.5)*5000)); //add shipping clerks for (int i = 0; i < 3; i++) shipMgr.add( new Employee("ShipClrk "+ new Integer(i).toString(), F +(float)(Math.random()-0.5)*5000));
Visual JTree private void addNodes(DefaultMutableTreeNode pnode, Employee emp) { DefaultMutableTreeNode node; Enumeration e = emp.elements(); while(e.hasMoreElements()) { Employee newEmp = (Employee)e.nextElement(); node = new DefaultMutableTreeNode(newEmp.getName()); pnode.add(node); addNodes(node, newEmp); }
Cost (Sum of Salaries) public void valueChanged(TreeSelectionEvent evt) { //called when employee is selected in tree llist TreePath path = evt.getPath(); String selectedTerm = path.getLastPathComponent().toString(); //find that employee in the composite Employee emp = boss.getChild(selectedTerm); //display sum of salaries of subordinates(if any) if(emp != null) cost.setText(new Float(emp.getSalaries()).toString()); }
Restrictions on Employee Classes e.g. never should have subordinates public void setLeaf(boolean b) { isLeaf = b; //if true, do not allow children } // public boolean add(Employee e) { if (! isLeaf) subordinates.addElement(e); return isLeaf; //false if unsuccessful }
Parent Reference It is perfectly possible for any composite element to remember its parent by including it as part of the constructor: public Employee(Employee _parent, String _name, float _salary) { name = _name; //save name salary = _salary; //and salary parent = _parent; //and parent subordinates = new Vector(); isLeaf = false; //allow children } This simplifies searching for particular members and moving up the tree when needed.