Harold Serrano

View Original

How to implement the Composite Design Pattern

Introduction

The Composite Design Pattern provides a way to create a tree structure that can handle a nested group of items.

For example, the image below shows an application with a main view, sub views and buttons.

Image 1. Application with Views

If we apply a Composite Design Pattern to the application above, we can treat the whole application as a tree-like structure (figure 2).

Image 2 Tree-like structure view

By placing each view in a tree-like structure, we have a unified access point to every view. Furthermore, we can call the same methods on the main view, sub-view or buttons.

For example, we can print the name of every child belonging to the Main View or to Sub-view 1 with the same method call.

Implementing the Composite Design Pattern

We are going to implement an Interface which will contain the following virtual methods:

  • add(): Adds a child to the tree
  • remove(): Removes a child from the tree
  • getChildName(): Prints the name of the node

Listing 1 shows the implementation of the interface named ComponentInterface

Listing 1. Interface implementation
using namespace std;

class Component;

class ComponentInterface{

  private:

  public:

  ComponentInterface(){};

  virtual void add(Component *uComponent)=0;
  virtual void remove(Component *uComponent)=0;


};

Interfaces are implemented as abstract classes in C++. Interfaces do not provide any method implementation and can not be instantiated. Thus, we need to create a subclass.

Listing 2 shows the implementation of the Component class, a subclass of ComponentInterface.

Listing 2. Component class
class Component:public ComponentInterface{

  private:

  public:

  //1. name of Component
  string name;

  //2. vector to store all Children
  vector<Component*> child;

  //3. constructor
  Component(string uName);

  ~Component();

  //4. these methods will be implemented by the subclasses
  virtual void add(Component *uComponent){};
  virtual void remove(Component *uComponent){};

  //5. function to print all children name
  void getChildName();


};

This subclass contains a vector of type Component (line 2). All children nodes are stored in this vector.

The Component class will not provide a concrete implementation of the add() and remove() methods. Instead, it will delegate this duty to its concrete subclasses (line 4).

Line 5 is a simple method which prints the name of the child node.

Implementing the Window Class

We are going to implement a subclass of the Component class. This class will be called WindowView. This is a concrete class and will implement the add() and remove().

This means that the WindowView class is allowed to have children nodes. You can add and remove as many nodes as you wish.

Listing 3 shows the implementation of the WindowView class. The add() method will store any child node into the vector called child.

Listing 3. WindowView class
void WindowView::add(Component *uComponent){

    child.push_back(uComponent);

}

Implementing the Button Class

Finally, we need to implement a subclass of the Component class which acts as a child node. This class is called ButtonView.

As opposed to the WindowView class, the ButtonView class is not allowed to have any children nodes. Therefore, the add() and remove() methods are not implemented in this class.

Composite Design Pattern in action

We are ready to see the Composite Design Pattern in action.

In our main() function, we are going to create three instances of the WindowView class (line 1). These instances are:

  • mainScreen
  • subScreen1
  • subScreen2

In line 2 we are going to create a play and stop button. They are instantiated as ButtonView and will act as children.

In line 3, we make subScreen1 and subScreen2 children of the mainScreen instance.

In line 4, we make the instances play and stop children of the subScreen1 instance.

Now, we want to print the name of all the children that a particular Component instance contains.

In line 5, we print the name of all the children of the mainScreen node. In this case, it will print the name of every child in the tree.

Optionally, we can also print the name of every child of the subScreen1 instance (line 6).

Listing 4 Main.c
int main(int argc, const char * argv[]) {

  //1. create Components
  Component *mainView=new WindowView("MainView");
  Component *subView1=new WindowView("SubView_1");

  Component *subView2=new WindowView("SubView_2");

  //2. create leaves
  Component *playButton=new ButtonView("Play");
  Component *stopButton=new ButtonView("Stop");

  //3. add children to the root node
  mainView->add(subView1);

  mainView->add(subView2);

  //4. add children to subviews
  subView1->add(playButton);
  subView1->add(stopButton);

  //5. print names of all main-view children
  mainView->getChildName();

  //6. print names of all subview children
  //subView1->getChildName();

  return 0;
}

As mentioned above, The Composite Design Pattern provides a way to create a tree structure that can handle a nested group of items.

By placing each view in a tree-like structure, we have a unified access point to every view. Furthermore, we can call the same methods on the main view, sub-view or buttons.

Source code

The source code for the Composite Design Pattern can be found here.

PS. Sign up to my newsletter and get development tips