Composite pattern is used to compose objects into tree structures to represent part-whole hierarchies.
Contents
- 1 Composite Pattern’s Intent
- 2 Composite Pattern’s Motivation
- 3 Composite Pattern’s Applicability
- 4 Composite Pattern’s Structure
- 5 Participants
- 6 Collaborations
- 7 Composite Pattern’s Consequences
- 8 Composite Pattern’s Implementation
- 9 Sample Code (Java Example)
- 10 Composite Pattern’s Known Uses
- 11 Related Patterns
- 12 Non-Software Example
Composite Pattern’s Intent
To compose objects into tree structures to represent part-whole hierarchies. Composite pattern lets client treat individual objects and compositions of objects uniformly.
Composite Pattern’s Motivation
There are times when a program needs to manipulate a tree data structure and it is necessary to treat both Branches as well as Leaf Nodes uniformly. Consider for example a program that manipulates a file system. A file system is a tree structure that contains Branches which are Folders as well as Leaf nodes which are Files.
Note that a folder object usually contains one or more file or folder objects and thus is a complex object where a file is a simple object. Note also that since files and folders have many operations and attributes in common, such as moving and copying a file or a folder, listing file or folder attributes such as file name and size, it would be easier and more convenient to treat both file and folder objects uniformly by defining a File System Resource Interface.
Composite Pattern’s 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.
Composite Pattern’s Structure
The structure of composite pattern is as shown below:
Participants
Following are the participants in composite pattern:
- Component: Declares the interface for objects in the composition. Declares an interface for accessing and managing its child components.
- Leaf: Represents leaf objects in the composition. A leaf has no children. Defines behavior for primitive objects in the composition.
- Composite: Defines behavior for components having children. Stores child components. Implements child-related operations in the composite
- Client: Manipulates objects in the composition through the Component
Collaborations
Clients use the Component class interface to interact with objects in the composite structure.
Composite Pattern’s Consequences
The composite pattern:
- Defines class hierarchies consisting of primitive objects and composite objects. Primitive objects can be composed into more complex objects, which in turn can be composed, and so on recursively.
- Makes the client simple. Clients can treat composite structures and individual objects uniformly.
- Makes it easier to add new kinds of components. Newly defined Composite or Leaf subclasses work automatically with existing structures and client code.
- Can make your design overly general. The disadvantage of making it easy to add new components is that it makes it harder to restrict the components of a composite. Sometimes you want a composite to have only certain components.
Composite Pattern’s Implementation
Following are the issues to consider when implementing composite pattern:
- Explicit parent references: Maintaining references from child components to their parent can simplify the traversal and management of a composite structure. The parent reference simplifies moving up the structure and deleting a component. Parent references also help support the Chain of Responsibility pattern.
- Sharing components: It’s often useful to share components, for example, to reduce storage requirements. But when a component can have no more than one parent, sharing components becomes difficult.
- Maximizing the Component interface: One of the goals of the Composite pattern is to make clients unaware of the specific Leaf or Composite classes they’re using. To attain this goal, the Component class should define as many common operations for Composite and Leaf classes as possible. The Component class usually provides default implementations for these operations, and Leaf and Composite subclasses will override them.
- Declaring the child management operations: Although the Composite class implements the Add and Remove operations for managing children, an important issue in the Composite pattern is which classes declare these operations in the Composite class hierarchy.
- Should Component implement a list of Components: You might be tempted to define the set of children as an instance variable in the Component class where the child access and management operations are declared. But putting the child pointer in the base class incurs a space penalty for every leaf, even though a leaf never has children.
- Child ordering: Many designs specify an ordering on the children of Composite. In the earlier Graphics example, ordering may reflect front-to-back ordering. If Composites represent parse trees, then compound statements can be instances of a Composite whose children must be ordered to reflect the program.
- Caching to improve performance: If you need to traverse or search compositions frequently, the Composite class can cache traversal or search information about its children. The Composite can cache actual results or just information that lets it short-circuit the traversal or search.
- Who should delete components: In languages without garbage collection, it’s usually best to make a Composite responsible for deleting its children when it’s destroyed. An exception to this rule is when Leaf objects are immutable and thus can be shared.
- What’s the best data structure for storing components: Composites may use a variety of data structures to store their children, including linked lists, trees, arrays, and hash tables. The choice of data structure depends (as always) on efficiency.
Sample Code (Java Example)
A diagram is a structure that consists of Objects such as Circle, Lines, Triangle etc and when we fill the drawing with color (say Red), the same color also gets applied to the Objects in the drawing. Here drawing is made up of different parts and they all have same operations.
Composite Pattern consists of following objects.
- Base Component – Base component is the interface for all objects in the composition, client program uses base component to work with the objects in the composition. It can be an interface or an abstract class with some methods common to all the objects.
- Leaf – Defines the behaviour for the elements in the composition. It is the building block for the composition and implements base component. It doesn’t have references to other Components.
- Composite – It consists of leaf elements and implements the operations in base component.
Here I am applying composite design pattern for the drawing scenario.
Base Component
Base component defines the common methods for leaf and composites, we can create a class Shape with a method draw(String fillColor) to draw the shape with given color.
//Shape.java
public interface Shape {
public void draw(String fillColor);
}
Leaf Objects
Leaf implements base component and these are the building block for the composite. We can create multiple leaf objects such as Triangle, Circle etc.
//Triangle.java
public class Triangle implements Shape {
@Override
public void draw(String fillColor) {
System.out.println("Drawing Triangle with color "+fillColor);
}
}
//Circle.java
public class Circle implements Shape {
@Override
public void draw(String fillColor) {
System.out.println("Drawing Circle with color "+fillColor);
}
}
Composite
A composite object contains group of leaf objects and we should provide some helper methods to add or delete leafs from the group. We can also provide a method to remove all the elements from the group.
//Drawing.java
import java.util.ArrayList;
import java.util.List;
public class Drawing implements Shape{
//collection of Shapes
private List<Shape> shapes = new ArrayList<Shape>();
@Override
public void draw(String fillColor) {
for(Shape sh : shapes)
{
sh.draw(fillColor);
}
}
//adding shape to drawing
public void add(Shape s){
this.shapes.add(s);
}
//removing shape from drawing
public void remove(Shape s){
shapes.remove(s);
}
//removing all the shapes
public void clear(){
System.out.println("Clearing all the shapes from drawing");
this.shapes.clear();
}
}
Notice that composite also implements component and behaves similar to leaf except that it can contain group of leaf elements.
Our composite pattern implementation is ready and we can test it with a client program.
//TestCompositePattern.java
public class TestCompositePattern {
public static void main(String[] args) {
Shape tri = new Triangle();
Shape tri1 = new Triangle();
Shape cir = new Circle();
Drawing drawing = new Drawing();
drawing.add(tri1);
drawing.add(tri1);
drawing.add(cir);
drawing.draw("Red");
drawing.clear();
drawing.add(tri);
drawing.add(cir);
drawing.draw("Green");
}
}
Composite Pattern’s Known Uses
Following are the examples in Java API where composite pattern is used:
- awt.Container#add(Component) (practically all over Swing thus)
- faces.component.UIComponent#getChildren() (practically all over JSF UI thus)
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.
Visitor localizes operations and behavior that would otherwise be distributed across Composite and Leaf classes.
Non-Software Example
The Composite composes objects into tree structures, and lets clients treat individual objects and compositions uniformly. Although the example is abstract, arithmetic expressions are Composites. An arithmetic expression consists of an operand, an operator (+ – * /), and another operand. The operand can be a number, or another arithmetic expression. Thus, 2 + 3 and (2 + 3) + (4 * 6) are both valid expressions.
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
Leave a Reply