Composite Pattern

Compose objects into tree structures to represent part-whole hierarchies. Client are able to ignore the difference between compositions of objects and individual objects.

Without this pattern, every component or object can be classified into one of the two categories:

  • Individual Components or
  • Composite Components. Composite components are composed of individual components or other composite components.

But the Composite pattern is useful in designing a common interface for both individual and composite components so that client programs can view both the individual components and groups of components uniformly.

composite.jpg

The "component" can be interface or abstract. Abstract class is more flexible as "Composite" specific methods can be moved to "Component". This frees the client from having to check to make sure that the "Component" is dealing with a "Leaf" or "Composite" in a method such as "addComponent".

An example with abstract Component:

public abstract class Component {
 
         public abstract void operation1();
 
        /*
Default implementation for addComponent.
This default implementation can cater for both Composite and Leaf object or 
just the Leaf object as it does not support this operation. 
In that case we need to overwrite it in Composite object which is a better approach.
        */
    public void addComponent(Component c){    
        if(c instanceof Leaf ) throws exception or handle softly!
    }
}
 
public class Composite extends Component {
        public void addComponent(){ //overwrite for itself
         ...
        }
 
        public void operation1() {
    }
}
 
public class Leaf extends Component{
    public void operation1() {
    }
}
 
public class Client {
    public void testit(Component c) {
        c.addComponent(c);    //client does not need to know the type of component
        c.operation1();
    }
}

An example with interface Component:

public interface Component {
    public void operation1();
}
 
public class Composite implements Component {
    public void addComponent(Component c){
    }
 
    public void operation1() {
    }    
}
 
public class Leaf implements Component  {
    public void operation1() {
    }
}
 
public class Client {
    public void testit(Component c) {
 
                // When using addComponent, client needs to know the type of Component
        if(c instanceof Composite){
            c = (Composite)c;
            ((Composite) c).addComponent(c);
        }
 
        c.operation1();
    }
}

Implementation:
Defining the child management interface at the root of the class hierarchy gives us transparency, but defining it the Composite class gives us safety, because any attempt to add or remove objects from leaves will be caught at compile-time in a statically typed language.

Related Patterns:

  • Often the component-parent link is used for a Chain of Responsibility
  • 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.
  • Flyweight lets you share components, but they can no longer refer to their parents.
  • Iterator can be used to traverse composites or having child ordering.
  • Visitor localizes operations and behavior that would otherwise be distributed across Composite and Leaf classes.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License