Open In App
Related Articles

Composite Design Pattern | JavaScript Design Patterns

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Report issue
Report

The Composite Design pattern is a way of organizing objects. It helps us handle different objects in a similar way when they are put together to create a structure with parts and wholes. These parts and wholes are like building blocks that can be split into smaller pieces and then put together to make a tree-like structure.

The composite design pattern allows us to build structures so that clients can work with both individual elements and collections of elements seamlessly and consistently and makes it easy to add new types of components (individual or composite) without modifying the client code promoting flexibility and reusability.

Components of the Composite Design Pattern

The components of the Composite Design Pattern include:

  • Component: The `Component` is an Abstract class (or sometimes Interface) that defines all the methods or common operations for both Leaf and Composite classes.

Note: JavaScript doesn’t support Interfaces directly. An alterative could be create a class and modify accordingly.

  • Leaf: The Leaf is a class that extends or implements the Component to provide the implementation for the declared methods in it. The Leaf class represents the individual components that do not have any child classes or nodes in the hierarchy.
  • Composite: The Composite is a class that represents the composite nodes that can hold leaf nodes. It implements the Component interface and provides methods to add, remove, and manipulate child components.
  • Client: The client manipulates the objects in the composition through the component interface.

Implementation of the Composite Design Pattern in JavaScript

Let us follow the below steps to implement a ‘File System’ using Composite Design Pattern.

Step 1: Create a Component class

Javascript

class Component {
  print() {
    throw new Error('Override this method');
  }
 
  size() {
    throw new Error('Override this method');
  }
}

                    

Explanation:

This class `Component` represents the class that declares the method definitions that can be provided with accurate implementations. In this Component class, I declared two methods namely `print` and `size` whose implementation is hidden and will be override by subclasses. These methods are assumed to be commonly shared with each individual part in the whole hierarchy.

Step 2: Create the Leaf class

Javascript

class File extends Component {
  constructor(name, size, location) {
    super();
    this.name = name;
    this.size = size;
    this.location = location
  }
 
  print() {
    console.log(`The File with the name ${this.name} whose size is ${this.size} KB, present is ${this.location}`);
  }
 
  size() {
    return this.size;
  }
}

                    

Explanation:

I have created the `File` class which represent individual component in the File System. This class extends the `Component` class and initializes a constructor with three parameters namely `name`, `size`, `location`. The methods `print` and `size` are overrided and accurately defined in this File class. The purpose of print method is to print the details of name, size and location of the file and the purpose of the size function is to return the size of the file.

Step 3: Create the Composite class

Javascript

class Folder extends Component {
  constructor(name) {
    super();
    this.name = name;
    this.files= [];
  }
 
  add(file) {
    this.files.push(file);
  }
 
  delete(file) {
    const idx = this.children.indexOf(file);
    if (index !== -1) {
      this.files.splice(idx, 1);
    }
  }
 
  print() {
    console.log(`Folder: ${this.name}`);
    this.files.forEach((file) => {
      file.print();
    });
  }
}

                    

Explanation:

The Folder class represents the `Composite` class whose purpose is to hold the leaf classes and define methods to `add`, `delete` and manipulate the hierarchy. I have defined constructor which initializes the name of the folder/directory and an array to hold the files of that folder. Followed by that, I have defined two methods names `add` and `delete` whose purpose is to add the file to the part-whole hierarchy and delete the file from the hierarchy respectively. The method `print` is also overrided to provide the specific implementation for the folder.

Step 4: Instance creation and usage

Javascript

const file = new File('document.txt', 10, 'c:/users/downloads');
const folder = new Folder('users');
folder.add(file);
 
const root = new Folder('Root');
root.add(folder);
 
console.log('File system structure:');
root.print();

                    

Output:

Screenshot-(1208)

output

Explanation:

In this step, I am creating instance for the File class as file along with the values of name, size and location. I have created a instance for the Folder class with the name ‘users’ and added the file to the folder using the add method. I have also created a instance called root which represents the root directory of the filesystem which holds all the directories of the system and finally used print method to display the overall File system structure.

Below is the diagram for the above example:


Composite-Design-Pattern

Advantages of the Composite Design Pattern in JavaScript Design Pattern

  • Flexibility: The Composite design pattern allows us to treat the individual components and composite objects uniformly. so, that provides flexibility to work with both components and composites similarly.
  • Simplified client code: As the composite design pattern provides a common interface i.e. component to interact with the methods, the client need not provide the different implementations to distinguish the individual components and composite objects.
  • Recursive Behavior: The Composite design pattern supports the recursive traversal and processing of the composite structure. This is particularly useful for operations that must be applied recursively to all components in the structure.
  • Simplified Addition of Components: The Composite design pattern makes it simple to add new components without modifying the client code.
  • Single Responsibility Principle: In this pattern, Each class (leaf or composite) has a single responsibility: either representing an individual object or a composition of objects. This adheres to the Single Responsibility Principle (SRP).

Disadvantages of the Composite Design Pattern in JavaScript Design Pattern

  • Complexity in implementation: The Composite design pattern is implemented in such a way that it should handle both individual components and composite objects which is quite complex to implement.
  • Limited Abstraction: The designing of the common interface can limit the abstraction level for some operations, it needs to be fairly general to accommodate different types of components.
  • Performance Considerations: Depending on the implementation, operations like traversing the composite structure can potentially have a performance impact, especially as the structure grows more extensive and more complex.


Last Updated : 31 Oct, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads