Introduction
This article shows two ways to compose objects into tree structures to represent part-whole hierarchies.
The first example explains how to use a simple Composite pattern of GoF (by using standard C++) and second one focuses on using the Qt Nokia Framework.
Background
The figure below shows a UML class diagram for the Composite Pattern:
For more information about Composite pattern, please see the following article from Wikipedia
or see the book of Gamma, Erich; Richard Helm, Ralph Johnson, John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software.
The Way for Using Composite Pattern with Standard C++
OK, now we need to build a tree structure to implement one hierarchy (an organizational structure). An organization can be made up of something like a chief executive officer (CEO) at the top with many different types of managers below him and more employees below the managers.
Finally there will be someone in the hierarchy who would be at the leaf level. By leaf we mean someone at the bottom most level. So there are two types of folks, one to whom people report and another to whom no one reports (see the figure at the beginning of article)
Let's assume that the client (for example a tax inspector) wants to know about the salaries of this organization. Overall salary is made up of individual salaries therefore all the employees should be able to return their own salary.
When it comes to Client who is looking for the salary of any departament
(of course, departament may be the whole company) then every employee should be able to return his salary and also the salary of all people under his supervision.
Using the composite pattern design is one of those innovations that would make life easier for people who want to organize
the construction of a hierarchical structure.
OK, let's create an UML (class diagram) for our example:
The above diagram represents a leaf node (Worker) and a parent node (Manager). Please note that a Manager could be a CEO, CTO or a VP etc.
This is also a very simple example of Inheritance in action with Polymorphism.
Now there are a few things that we need to see carefully in this diagram
- Component: contains only an interface - printSalary a pure virtual method, which has to be implemented by a derived class (Worker & Manager)
- Manager: contains itself and contains the worker(s). The Manager MUST always be the root of the tree
- Worker: contains only itself
The following example, written in C++, implements a Component class, which can be either a Worker or a Manager (composition of several Workers). Every component can print his salary.
class Component
{
public:
Component(std::string name, double salary)
: m_fullName(name), m_valueSalary (salary) {}
virtual void printSalary(int level) = 0;
std::string m_fullName;
double m_valueSalary;
};
class Worker : public Component
{
public:
Worker(std::string name , double salary): Component(name,salary)
{
}
void printSalary(int level)
{
for(int j=0; j < level; ++j) cout << "\t";
cout << "Worker : " <<
m_fullName.c_str() << ",salary: " <<
m_valueSalary << "$\n";
}
};
class Manager: public Component
{
public:
Manager(std::string name , double salary) : Component(name,salary)
{
}
void add(Component *cmp)
{
m_children.push_back(cmp);
}
void printSalary(int level)
{
for(int j=0; j < level; ++j) cout << "\t";
cout << "Manager : " << this->m_fullName.c_str() <<
",salary: " << m_valueSalary << "$\n";
if(!m_children.empty())
{
for(int x=0; x < level; ++x) cout << "\t";
cout << "Subordinates of " <<
m_fullName.c_str() << ":\n";
++level;
for (int i = 0; i < m_children.size(); ++i)
m_children[i]->printSalary(level);
}
}
private:
vector < Component * > m_children;
};
int main()
{
Manager president ("Gerard Butcher", 1000000.0);
Manager manager_production_department ("John Smith",400000.0);
Manager manager_engineering_department ("Michael Biner",400000.0);
Manager manager_quality_control_department ("David Jaskson",280000.0);
Manager manager_sales_management_division ("Tom Vilow",270000.0);
Manager manager_general_affairs_department ("Janet Teyllor" ,200000.0);
Manager team_leader_RandD ("Jorge Creig", 250000.0);
Manager team_leader_QA ("Arnold Lambero", 200000.0);
Worker software_developer1 ("Andrey Lapidos", 200000.0);
Worker software_developer2 ("Maxim Laertsky", 240000.0);
Worker tester ("Miki Minaj", 130000.0);
president.add(&manager_production_department);
president.add(&manager_engineering_department);
president.add(&manager_quality_control_department);
president.add(&manager_sales_management_division);
president.add(&manager_general_affairs_department );
manager_engineering_department.add(&team_leader_RandD);
manager_engineering_department.add(&team_leader_QA );
team_leader_RandD.add(&software_developer1);
team_leader_RandD.add(&software_developer2);
team_leader_QA.add(&tester);
cout << "The hierarchy of the company,\ni.e. president and all who is under his supervision :\n\n" ;
president.printSalary(0);
cout << '\n';
}
The way for using Composite Pattern with Qt4 Cross-Platform Framework
As we know in Qt exists the QObject class. The QObject class is the base class of all Qt objects.
QObject derived class can be quite useful because we can express the relationship parent-child between objects of classes inherited from QObject.
With this class and its methods (setParent, findChildren and parent), which I will explain below, we can abandon the previous design
(with the implementation of two different classes), using instead a new design (with only one class in both cases: Worker and Manager).
As in the previous solution, the root of tree (i.e., the top level of an organizational hierarchy) QObject will have lots of children, but no parent.
The simplest QObjects (i.e., the leaf nodes of this tree) will each have a parent, but no children.
Client code can recursively deal with each node of the tree.
So we will define the class diagram...
The QObject public interface allows us to build up a tree-like representation of the organization with code that instantiates an WorkerOrManager
and then calls setParent()
to add it to the appropriate child list.
class WorkerOrManager : public QObject
{
public:
WorkerOrManager(QString name , double salary)
{
m_fullName = name;
m_valueSalary = salary;
}
void printSalary(int level)
{
for(int j=0; j < level; ++j) std::cout << "\t";
std::cout << "Worker : " <<
m_fullName.toStdString() << ",salary: " <<
m_valueSalary << "$\n";
QList<WorkerOrManager*> children =
findChildren<WorkerOrManager*>();
if(!children.isEmpty())
{
for(int j=0; j < level; ++j) std::cout << "\t";
std::cout << "Subordinates of " <<
m_fullName.toStdString() << ":\n";
++level;
for (int i = 0; i < children.size(); ++i)
{
if(children[i]->parent() == this)
{
children[i]->printSalary(level);
}
}
}
}
private:
QString m_fullName;
double m_valueSalary;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerOrManager president ("Gerard Butcher", 1000000.0);
WorkerOrManager manager_production_department ("John Smith",400000.0);
WorkerOrManager manager_engineering_department ("Michael Biner",400000.0);
WorkerOrManager manager_quality_control_department ("David Jaskson",280000.0);
WorkerOrManager manager_sales_management_division ("Tom Vilow",270000.0);
WorkerOrManager manager_general_affairs_department ("Janet Teyllor" ,200000.0);
WorkerOrManager team_leader_RandD ("Jorge Creig", 250000.0);
WorkerOrManager team_leader_QA ("Arnold Lambero", 200000.0);
WorkerOrManager software_developer1 ("Andrey Lapidos", 200000.0);
WorkerOrManager software_developer2 ("Maxim Laertsky", 240000.0);
WorkerOrManager tester ("Miki Minaj", 130000.0);
manager_production_department.setParent(&president);
manager_engineering_department.setParent(&president);
manager_quality_control_department.setParent(&president);
manager_sales_management_division.setParent(&president);
manager_general_affairs_department.setParent(&president);
team_leader_RandD.setParent(&manager_engineering_department);
team_leader_QA.setParent(&manager_engineering_department);
software_developer1.setParent(&team_leader_RandD);
software_developer2.setParent(&team_leader_RandD);
tester.setParent(&team_leader_QA);
cout << "The hierarchy of the company,\ni.e. president and all who is under his supervision :\n\n" ;
president.printSalary(0);
return a.exec();
}
Whenever we have two objects QObject (for example objA and objB) and we want to make one object the child of another, we simply call the QObject::setParent
For example, after calling the objB.setParent(objA)
,the objA
will be parent of objB. And by using the QObject::parent()
method, we can check who is the parent object
Please note that due to the following method, we can get access to all the descendants of the object:
QList<T> QObject::findChildren ( const QString & name = QString() ) const
As stated in the Qt documentation: "This method returns the children of this object that can be cast
to type T and that have names matching the regular expression regExp, or an empty list if there are no such objects. The search is performed recursively"
In brief, by using QObject
class and the above mentioned methods, we can abandon the use of standard Composite Pattern and use the specific Qt-design,
as described in this section.
Summary
Note that in the two cases with different designs we have the same result, i.e. for the same input we get the same output:
History
- 10nd April 2012: Initial post