Last Updated on September 24, 2023 by KnownSense
The Composite Pattern is a structural design pattern to compose objects into tree-like structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly.
In essence, the Composite Pattern creates a unified interface for both individual objects (leaf nodes) and composite objects (nodes that can contain other objects). This enables clients to interact with the entire hierarchy of objects without needing to distinguish between them, simplifying the client code.
In document processing applications, you often deal with complex documents that can contain various elements such as text paragraphs, tables, images, lists, and more. These elements can be nested within each other, and you need to perform operations like rendering, formatting, searching, or printing on the entire document or its parts efficiently.
The Composite Pattern can be applied to represent documents as hierarchical structures, making it easier to work with and manipulate them.
Benefits
- Hierarchical Structure Handling: The Composite Pattern is ideal for representing hierarchical structures like directories and files, organization hierarchies, or the structure of a graphical user interface. It allows you to treat individual objects (leaf nodes) and compositions of objects (composite nodes) uniformly, simplifying the client code.
- Uniform Interface: It provides a consistent interface for both leaf and composite objects. This makes the client code simpler since it can interact with all objects in the hierarchy in the same way, regardless of whether they are individual objects or compositions.
- Simplifies Client Code: With the Composite Pattern, client code can be simplified as it doesn’t need to distinguish between individual objects and compositions. This leads to more generic and maintainable code.
- Flexibility: It allows you to create complex structures by composing simple objects. You can easily add or remove objects from the hierarchy without affecting the client code. This makes the system more flexible and extensible.
- Recursive Operations: You can perform operations recursively on the entire hierarchy by implementing them in the composite objects. For example, you can calculate the total cost of a product and all its sub-products in a composite structure.
- Scalability: The pattern allows you to build complex structures from simpler components. As your application evolves, you can create new composite objects to represent new structures without having to modify existing code.
- Separation of Concerns: It promotes a separation of concerns by keeping the logic for manipulating the hierarchy within the composite objects. This separation makes the codebase more organized and easier to maintain.
- Reduced Code Duplication: Since common operations are implemented in the composite objects, you avoid code duplication across different parts of the hierarchy. This leads to more efficient and maintainable code.
- Clear Structure: The Composite Pattern makes the structure of the composite objects explicit, which can improve code readability and understanding. It’s easier to see how the objects are organized within the hierarchy.
- Compatible with Other Patterns: The Composite Pattern can be combined with other design patterns, such as the Decorator Pattern, to add functionality to individual objects within the hierarchy.
UML of Composite Pattern
Implementation
Let’s create the document processing applications using composite pattern
Step1: Create component interface that defines the common render
method for all document elements.
interface DocumentElement {
void render();
}
Step2: Create leaf elements that implement the DocumentElement interface.
class TextParagraph implements DocumentElement {
private String text;
public TextParagraph(String text) {
this.text = text;
}
public void render() {
System.out.println("Text Paragraph: " + text);
}
}
class Image implements DocumentElement {
private String imageURL;
public Image(String imageURL) {
this.imageURL = imageURL;
}
public void render() {
System.out.println("Image: " + imageURL);
}
}
Step3: Create composite element that can contain other document elements. It has an addElement method to add elements to the document and a render method to render all elements within the document. we can also add other methods like getList, delete etc. according to need
class Document implements DocumentElement {
private List<DocumentElement> elements = new ArrayList<>();
public void addElement(DocumentElement element) {
elements.add(element);
}
public void render() {
for (DocumentElement element : elements) {
element.render();
}
}
}
Step4: In the main
method, we create a complex document structure with nested subsections, paragraphs, and images. When we call document.render()
, it recursively calls render
on all elements, rendering the entire document and its subcomponents.
public class CompositePattern {
public static void main(String[] args) {
// Create a complex document
Document document = new Document();
document.addElement(new TextParagraph("Introduction to Composite Pattern"));
document.addElement(new Image("image1.jpg"));
// Create a subsection within the document
Document subsection = new Document();
subsection.addElement(new TextParagraph("Subsection Title"));
subsection.addElement(new TextParagraph("Subsection Content"));
document.addElement(subsection);
// Render the entire document
document.render();
}
Conclusion
In summary, the Composite Pattern is used to create hierarchical structures of objects while providing a consistent way to work with individual objects and their compositions. It promotes code reusability and simplifies the handling of complex object structures.