Composite Design Pattern in C++



Composite Design Pattern is a structural design pattern that allows you to compose objects into tree like structures that represent part-whole hierarchies. This pattern lets clients treat any individual object and a composition of objects uniformly.

Sounds complicated? Let's break it down with an example.

  • Suppose you are building a graphic design application which allows users to create and manipulate various shapes like circles, rectangles, and lines.
  • You also want to allow user to group the shapes together to form new designs(like a house made of rectangles and triangles, or a tree made of circles and lines).
  • With the Composite Design Pattern, you can treat both individual shapes and groups of shapes uniformly. This means that you can perform operations like move, resize, or draw on both single shapes and groups of shapes without worrying about their specific types.
Composite Design Pattern Example

Components of Composite Design Pattern

The Composite Pattern is made up of multiple components. Take a look at the following image. Here, we can see the four main components of the Composite Design Pattern. The Component is an interface that has common methods for both Leaf and Composite. The Leaf is like a loner who doesn't have any children, while the Composite is like a parent who can have multiple children (which can be either Leaf or other Composite). And, lastly we have the Client, which is like the boss who interacts with the Component interface to perform operations on both Leaf and Composite.

Components of Composite Pattern

Implementation of Composite Design Pattern in C++

Now, that we understand the components of the Composite Design Pattern, let's see how we can implement it in C++.

In this implementation, we will implement a simple File System where we have Files and Directories. A File is a Leaf and a Directory is a Composite that can contain multiple Files and other Directories.

File System Example Composite Design Pattern

Steps to Implement Composite Design Pattern

Let's look at the steps to implement the File System using the Composite Design Pattern in C++:

First, create a Component interface that has all the common methods. Next, create a Leaf class that implements the Component interface. Then, create a Composite class that also implements the Component interface and has a list to store its children. Finally, create a Client class that interacts with the Component interface to perform operations on both Leaf and Composite.

Steps Composite Design Pattern

The code in image is just for representation purpose. The complete code is given below.

C++ Code for Composite Design Pattern

In this code, we will define a FileSystemComponent interface that has a method showDetails(). The File class implements this interface and represents a leaf node in the file system. The Directory class also implements the FileSystemComponent interface and represents a composite node that can contain multiple children (both files and directories).

Following is the complete C++ code for the Composite Design Pattern implementation of a File System −

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

// Component
class FileSystemComponent {
public:
   virtual void showDetails() = 0;
};

// Leaf
class File : public FileSystemComponent {
private:
   string name;
public:
   File(string fileName) : name(fileName) {}
   void showDetails() {
      cout << "File: " << name << endl;
   }
};

//  Composite 
class Directory : public FileSystemComponent {
private:
   string name;
   vector<shared_ptr<FileSystemComponent>> children;
public:
   Directory(string dirName) : name(dirName) {}
   void add(shared_ptr<FileSystemComponent> component) {
      children.push_back(component);
   }
   void showDetails() {
      cout << "Directory: " << name << endl;
      for (auto child : children) {
         child->showDetails();
      }
   }
};

// Client
int main() {
   shared_ptr<FileSystemComponent> file1 = make_shared<File>("File1.txt");
   shared_ptr<FileSystemComponent> file2 = make_shared<File>("File2.txt");
   shared_ptr<FileSystemComponent> dir1 = make_shared<Directory>("Dir1");
   shared_ptr<FileSystemComponent> dir2 = make_shared<Directory>("Dir2");

   dir1->add(file1);
   dir2->add(file2);
   dir2->add(dir1);

   dir2->showDetails();

   return 0;
}

Following is the output of the above code −

Directory: Dir2
File: File2.txt
Directory: Dir1
File: File1.txt

Advantages and Disadvantages of Composite Design Pattern

Following are the advantages and disadvantages of using the Composite Design Pattern −

Advantages Disadvantages
Makes code easier to use. Can make code harder to understand.
Easy to add new parts. Slow if there are many parts.
Helps reuse code. Can be tricky to test and fix bugs.
Good for building big structures. Can make things too complicated.
Easy to update code. Confusing to see how everything fits.
Can add new things without changing old code. Uses more memory.

When to Use Composite Design Pattern?

You should consider using the Composite Design Pattern in the following scenarios −

  • When you have a part-whole hierarchy in your application.
  • To represent tree structures, such as file systems or organizational hierarchies.
  • For treating individual objects and compositions of objects uniformly.
  • While simplifying client code by allowing it to interact with a single interface.
  • To add new types of components without changing existing code.
  • When you want to manage complex structures more easily.
  • Performing operations on both individual objects and groups of objects.

Real World Examples of Composite Design Pattern

Following are some real-world examples of the Composite Design Pattern −

Real World Applications Composite Design Pattern

Conclusion

In this chapter, we learned about the Composite Design Pattern, its components, implementation in C++, advantages, disadvantages, and when to use it. The Composite Design Pattern is a useful tool for managing complex structures and hierarchies in software development. By using this pattern, we can simplify our code and make it more easy to maintain and also extend in the future.

Advertisements