Composite Design Pattern in JavaScript

1. Definition

The Composite Design Pattern allows you to compose objects into tree-like structures to represent part-whole hierarchies. With this pattern, individual objects and compositions are treated uniformly.

2. Problem Statement

Suppose you are building a graphic design application. You have primitive elements like Circle, Rectangle, and more complex elements like Group which can contain multiple shapes. How do you treat both individual shapes and their compositions uniformly, so that they can all be rendered (or have other operations performed on them) in a consistent manner?

3. Solution

Use the Composite Design Pattern. Define a unified interface for individual objects and their compositions. This way, the client code can treat individual objects and compositions uniformly.

4. Real-World Use Cases

1. Graphic editors where shapes can be grouped and treated as one.

2. Organizational structures (e.g., an employee hierarchy where an individual might be a manager or a leaf node).

3. File systems with files and directories.

5. Implementation Steps

1. Define the component interface with default behaviors.

2. Create leaf components that implement the component interface.

3. Create composite components that can hold and manage child components.

4. Ensure that the client code can work with both leaf and composite components uniformly.

6. Implementation in JavaScript

// Component interface
function GraphicComponent() {
    this.draw = function() {};
    this.add = function(component) {};
    this.remove = function(component) {};
}
// Leaf
function Circle() {}
Circle.prototype = Object.create(GraphicComponent.prototype);
Circle.prototype.draw = function() {
    return "Drawing a circle";
};
// Composite
function GraphicGroup() {
    this.children = [];
}
GraphicGroup.prototype = Object.create(GraphicComponent.prototype);
GraphicGroup.prototype.draw = function() {
    return this.children.map(child => child.draw()).join("\n");
};
GraphicGroup.prototype.add = function(component) {
    this.children.push(component);
};
GraphicGroup.prototype.remove = function(component) {
    const index = this.children.indexOf(component);
    if (index !== -1) {
        this.children.splice(index, 1);
    }
};
// Client Code
const circle1 = new Circle();
const circle2 = new Circle();
const group = new GraphicGroup();
group.add(circle1);
group.add(circle2);
console.log(group.draw());

Output:

Drawing a circle
Drawing a circle

Explanation:

1. GraphicComponent is the base component defining the interface that all components, leaf or composite, should adhere to.

2. Circle is a leaf component. It implements the draw method.

3. GraphicGroup is a composite component. It can contain other graphics, be they leafs or composites. It overrides methods like draw, add, and remove.

4. In the client code, we created two circles and added them to a group. When we draw the group, it draws all its children.

7. When to use?

Use the Composite pattern when:

1. You want to represent part-whole hierarchies of objects.

2. You want the client to ignore the difference between compositions of objects and individual objects.


Comments