Composite Pattern
Introduction
The Composite pattern is a structural design pattern that lets you compose objects into tree structures to represent part-whole hierarchies. This pattern is useful for treating individual objects and compositions of objects uniformly.
Purpose
- To compose objects into tree structures to represent part-whole hierarchies.
- To let clients treat individual objects and compositions of objects uniformly.
Use Cases
- When you want to represent part-whole hierarchies of objects.
- When you want clients to ignore the difference between compositions of objects and individual objects.
Advantages/Disadvantages
Advantages:
- Simplifies the client code, as it can treat composite structures and individual objects the same way.
- Makes it easier to add new kinds of components.
Disadvantages:
- Can make your design overly general. It can be difficult to restrict the components of a composite to only certain types.
Implementation in JavaScript
class Component {
operation() {}
}
class Leaf extends Component {
operation() {
return 'Leaf';
}
}
class Composite extends Component {
constructor() {
super();
this.children = [];
}
add(component) {
this.children.push(component);
}
operation() {
return `Branch(${this.children
.map((child) => child.operation())
.join('+')})`;
}
}
// Usage
const leaf1 = new Leaf();
const leaf2 = new Leaf();
const composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);
console.log(composite.operation()); // "Branch(Leaf+Leaf)"
Practical Example
Let’s say you’re building a backend system for a file directory where both individual files and directories (which can contain other files or directories) need to be treated similarly.
class FileSystemComponent {
getSize() {}
}
class File extends FileSystemComponent {
constructor(size) {
super();
this.size = size;
}
getSize() {
return this.size;
}
}
class Directory extends FileSystemComponent {
constructor() {
super();
this.components = [];
}
add(component) {
this.components.push(component);
}
getSize() {
return this.components.reduce(
(total, component) => total + component.getSize(),
0,
);
}
}
// Usage
const file1 = new File(100);
const file2 = new File(200);
const directory = new Directory();
directory.add(file1);
directory.add(file2);
console.log(directory.getSize()); // 300 (sum of file sizes)
In this example, FileSystemComponent
is the abstract class for both files and directories. File
and Directory
classes extend it, where Directory
can have a collection of FileSystemComponent
objects, allowing directories to contain files or other directories. This pattern lets you treat both files and directories uniformly.