Introduction
This article introduces a reusable C++ template class that can be easily used in place of a Composite Design Pattern implementation. It has the following benefits:
- It eliminates the repeated task of pattern's skeleton implementation.
- Even novice developers with a brief idea of Composite pattern's structure can use it very easily.
- Code re-usability in-turn results in reduced bugs.
- The template class can be applied to existing classes also without much problem, to make them composite.
[Concept courtesy: From Patterns to Components -Doctoral thesis by Karine Arnout]
Background
The Composite Design Pattern is one among the 23 popular Design Patterns identified by the Gang of Four (GoF). The traditional implementation of the Composite pattern can be found here.
Using this traditional style, every time you will have to make the container infrastructure for the Composite
class. Also, note that the Component
class doesn't require the Container
functions like Add()
, Remove()
, GetChild()
, etc. They are required only in the Composite
class.
The template class introduced in this article helps you to focus on your Business logic which you are going to implement in the Component
, Composite
& Leaf
classes rather than drafting the design infrastructure like Add()
, Remove()
, GetChild()
, etc. in the Composite
class every time.
Limitations of this New Technique
- You will have to use the Composite.h header file in your application.
- This header file uses the STL headers such as
<vector>
& <algorithm>
; if you don't like to use STL, you will have to modify the template class for your purpose. - You will have to make an inheritance contract in your
Composite
class which relates it to your Component
class.
Using the Code
The following code has two sections:
Section 1 is the implementation of the Composite<>
class. It is a very simple class that you can modify easily by yourself if required..
Section 2 is an example program to demonstrate the usage of the Composite<>
class in a simple Electronic Components simulation program.
Section 1 - Composite Class
#ifndef COMPOSITE_H_
#define COMPOSITE_H_
#include <vector>
#include <algorithm>
namespace GoFPatterns
{
using namespace std;
template<typename Component>
class Composite: public Component
{
protected:
vector<Component*> children; public:
typedef typename vector<Component*>::iterator ChildIterator;
void AddChild(Component* child)
{
ChildIterator itr = find(children.begin(),children.end(),child);
if( itr == children.end())
{
children.push_back(child);
}
}
void RemoveChild(Component* child)
{
ChildIterator itr = find(children.begin(),children.end(),child);
if( itr != children.end())
{
children.erase(itr);
}
}
void Clear()
{
children.clear();
}
virtual ~Composite()
{
}
}; }#endif /* COMPOSITE_H_ */
EXAMPLE PROGRAM
#include <iostream>
#include "Composite.h"
namespace GoFExample
{
using namespace std;
using namespace GoFPatterns;
class ElectronicComponent
{
public:
virtual void PrintSpec(ostream& ostr, string prefix = "")= 0;
virtual void DoFunction(float& voltage, float& current) = 0;
};
class Resistor:public ElectronicComponent
{
float resistance_Ohm; Resistor(){}
public:
Resistor(float ohm)
:resistance_Ohm(ohm)
{}
void PrintSpec(ostream& ostr, string prefix = "")
{
ostr<<prefix<<"Resistor ("<< resistance_Ohm <<" Ohm )"<<endl;
}
void DoFunction(float& voltage, float& current)
{
cout<<endl<<"Resistor Input ("<<voltage<<" V ,"<<current<<" Amp)
Resistance = "<< resistance_Ohm <<endl;
}
};
class Capacitor:public ElectronicComponent
{
float capacitance_muF; Capacitor(){}
public:
Capacitor(float muF)
:capacitance_muF(muF)
{
}
void PrintSpec(ostream& ostr, string prefix = "")
{
ostr<<prefix<<"Capacitor ("<< capacitance_muF <<" muF )"<<endl;
}
void DoFunction(float& voltage, float& current)
{
cout<<endl<<"Capacitor Input ("<<voltage<<" V ,"<<current<<" Amp)
Capacitance ="<< capacitance_muF<<endl;
}
};
class ICChip:public Composite<ElectronicComponent>
{
public:
void PrintSpec(ostream& ostr, string prefix = "")
{
ostr<<prefix<<"ICChip With "<< children.size()<<
" Components " <<endl;
for( unsigned int i = 0; i < children.size(); i++)
{
ostr<<prefix;
children[i]->PrintSpec(ostr,"\t");
}
}
void DoFunction(float& voltage, float& current)
{
cout<<endl<<"ICChip Input ("<<voltage<<" V ,"<<current<<" Amp)";
for( unsigned int i = 0; i < children.size(); i++)
{
children[i]->DoFunction(voltage,current);
}
}
};
class PCB:public Composite<ElectronicComponent>
{
public:
void PrintSpec(ostream& ostr, string prefix = "")
{
ostr<<prefix<<"PCB With "<< children.size()<< " Components " <<endl;
for( unsigned int i = 0; i < children.size(); i++)
{
ostr<<prefix;
children[i]->PrintSpec(ostr,"\t");
}
}
void DoFunction(float& voltage, float& current)
{
cout<<endl<<"PCB Input ("<<voltage<<" V ,"<<current<<" Amp) ";
for( unsigned int i = 0; i < children.size(); i++)
{
children[i]->DoFunction(voltage,current);
}
}
};
}
using namespace GoFExample;
int main()
{
Resistor r1(50), r2(70); Capacitor c1(200),c2(300);
ICChip ic1; ic1.AddChild(new Resistor(2000)); ic1.AddChild(new Capacitor(1000));
PCB pcb1; pcb1.AddChild(&r1);
pcb1.AddChild(&c1);
pcb1.AddChild(&c2);
pcb1.AddChild(&ic1);
pcb1.AddChild(&ic1);
cout<<"\n=========== Printing the PCB Spec =========="<<endl;
pcb1.PrintSpec(cout);
float v =110, i = 5;
cout<<"\n=========== DoFunction(110,5) of PCB =========="<<endl;
pcb1.DoFunction(v,i);
cout<<"\n=========== Removing c2 from PCB =========="<<endl;
pcb1.RemoveChild(&c2);
cout<<"\n=========== Printing the PCB Spec =========="<<endl;
pcb1.PrintSpec(cout);
return 0;
}
PROGRAM OUTPUT
=========== Printing the PCB Spec ==========
PCB With 4 Components
Resistor (50 Ohm )
Capacitor (200 muF )
Capacitor (300 muF )
ICChip With 2 Components
Resistor (2000 Ohm )
Capacitor (1000 muF )
=========== DoFunction(110,5) of PCB ==========
PCB Input (110 V ,5 Amp)
Resistor Input (110 V ,5 Amp) Resistance = 50
Capacitor Input (110 V ,5 Amp) Capacitance =200
Capacitor Input (110 V ,5 Amp) Capacitance =300
ICChip Input (110 V ,5 Amp)
Resistor Input (110 V ,5 Amp) Resistance = 2000
Capacitor Input (110 V ,5 Amp) Capacitance =1000
=========== Removing c2 from PCB ==========
=========== Printing the PCB Spec ==========
PCB With 3 Components
Resistor (50 Ohm )
Capacitor (200 muF )
ICChip With 2 Components
Resistor (2000 Ohm )
Capacitor (1000 muF )
Points of Interest
When I thought about componentizing the Composite<>
pattern, I expected I will reach a complex template class to get to the point. But as you can see, the Composite<>
class is as simple as you might think it is not required. But in reality, sometimes simplest solutions do the trick to get you where you want.
In this case, on the library side, the inheritance of Composite<>
to its typename (Component
) does the magic.
template<typename Component>
class Composite: public Component
{
On the client side, the inheritance of Composite
class from Composite<Component>
quickly turns it to a Container
with those AddChild()
, RemoveChild()
, etc. functions.
class ICChip:public Composite<ElectronicComponent>
{
I added the AddChild ()
, RemoveChild()
& Clear()
methods just for not exposing the children <vector>
.
History
- 17-April-2009: Initial version