Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / QT

Two Ways to Realise the Composite Pattern in C++ and Qt

Rate me:
Please Sign up or sign in to vote.
4.83/5 (25 votes)
17 May 2012CPOL4 min read 70.5K   497   24   24
In this article I show the Object-Oriented Implementation of tree, by using Composite Design Pattern, C++ and Qt Framework. I will also explain why we do not use (although you can) in Qt the standard design of Composite Pattern as described in GoF.

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:

Image 1

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:

Image 2

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.

C++
class Component
{
  public:
    Component(std::string name, double salary) 
          : m_fullName(name), m_valueSalary (salary) {}

    virtual void printSalary(int level) = 0;
    
    //Of course these data members should be private, 
    //but I did not wish to "litter" this class with 
    //superfluous functions-interfaces,
    //therefore I have left these data members of class to be public
    std::string            m_fullName;
    double                 m_valueSalary;
};

/** "Leaf" */
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";
    }
};

/** "Composite" */
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:
    // The manager can have a number of people(managers or workers) 
    // under his/her supervision 
    // and that is the reason we have the vector here (for 
    // navigating a hierarchical organisation, 
    // for typing an individual salary)
    vector < Component * > m_children;
};

int main()
{   
    //Let's define a big chief
    Manager president ("Gerard Butcher", 1000000.0);
    
    //Let's define several average chiefs
    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);
    
    //Let's define several managers of a engineering department
    Manager team_leader_RandD ("Jorge Creig", 250000.0); 
    Manager team_leader_QA ("Arnold Lambero", 200000.0); 
    
    //Let's define several engineers of a engineering department
    Worker software_developer1 ("Andrey Lapidos", 200000.0);
    Worker software_developer2 ("Maxim  Laertsky", 240000.0);
    Worker tester ("Miki  Minaj", 130000.0);
    
    //Now we will add the number of persons as assistants of president
    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 );

    //Now we will add the number of persons as assistants of manager engineering department
    manager_engineering_department.add(&team_leader_RandD);
    manager_engineering_department.add(&team_leader_QA );

    //Now we will add the number of persons as assistants of team leader the R&D
    team_leader_RandD.add(&software_developer1);
    team_leader_RandD.add(&software_developer2);

    //Now we will add the tester as assistant of team leader the QA
    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...

Image 3

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.

C++
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*>();

        //Here, we want to check if the object is a manager
        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)
            {
                //We deduce data only about direct subordinates
                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);

    //Let's define a big chief
    WorkerOrManager president ("Gerard Butcher", 1000000.0);

    //Let's define several average  chiefs
    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);

    //Let's define several managers of a engineering department
    WorkerOrManager team_leader_RandD ("Jorge Creig", 250000.0);
    WorkerOrManager team_leader_QA ("Arnold Lambero", 200000.0);

    //Let's define several engineers of a engineering department
    WorkerOrManager software_developer1 ("Andrey Lapidos", 200000.0);
    WorkerOrManager software_developer2 ("Maxim  Laertsky", 240000.0);
    WorkerOrManager tester ("Miki  Minaj", 130000.0);

    //Now we will add the number of persons as assistants of president
    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);

    //Now we will add the number of persons as assistants of manager engineering department
    team_leader_RandD.setParent(&manager_engineering_department);
    team_leader_QA.setParent(&manager_engineering_department);

    //Now we will add the number of persons as assistants of team leader the R&D
    software_developer1.setParent(&team_leader_RandD);
    software_developer2.setParent(&team_leader_RandD);

    //Now we will add the tester as assistant of team leader the QA
    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:

Image 4

History

  • 10nd April 2012: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Canada Canada
Mr.Volynsky Alex is a Software Engineer in a leading software company. Alex is skilled in many areas of computer science. He has over 14 years of experience in the design & development of applications using C/C++/STL, Python, Qt, MFC, DirectShow, JavaScript, VBScript, Bash and of course - C#/.NET.

In addition, Alex is the active member of Intel® Developer Zone (he was awarded by Intel® Green Belt for his active contribution to the Intel Developer Zone community for developers using Intel technology).

Alex is also interested in the Objective-C development for the iPad/iPhone platforms and he is the developer of the free 15-puzzle game on the App Store.

Overall, Alex is very easy to work with. He adapts to new systems and technology while performing complete problem definition research.

His hobbies include yacht racing, photography and reading in multiple genres.
He is also fascinated by attending computer meetings in general, loves traveling, and also takes pleasure in exercising and relaxing with friends.

Visit his C++ 11 blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
Sharjith19-Feb-15 15:41
professionalSharjith19-Feb-15 15:41 
GeneralRe: My vote of 5 Pin
Volynsky Alex19-Feb-15 21:30
professionalVolynsky Alex19-Feb-15 21:30 
Questioncomponent class should not be a abstract class (method should not be a pure virtual function) Pin
Member 1007345323-May-13 6:50
Member 1007345323-May-13 6:50 
SuggestionRe: component class should not be a abstract class (method should not be a pure virtual function) Pin
Volynsky Alex23-May-13 9:58
professionalVolynsky Alex23-May-13 9:58 
GeneralMy vote of 5 Pin
mk4you713-Jun-12 4:54
mk4you713-Jun-12 4:54 
GeneralRe: My vote of 5 Pin
Volynsky Alex14-Jun-12 15:09
professionalVolynsky Alex14-Jun-12 15:09 
GeneralMy vote of 5 Pin
Steph_Iv28-May-12 10:32
Steph_Iv28-May-12 10:32 
GeneralRe: My vote of 5 Pin
Volynsky Alex14-Jun-12 15:09
professionalVolynsky Alex14-Jun-12 15:09 
GeneralMy vote of 5 Pin
EMogilevsky26-May-12 4:28
EMogilevsky26-May-12 4:28 
GeneralRe: My vote of 5 Pin
Volynsky Alex14-Jun-12 15:10
professionalVolynsky Alex14-Jun-12 15:10 
GeneralMy vote of 5 Pin
Gerard Forestier24-May-12 22:15
Gerard Forestier24-May-12 22:15 
QuestionMy vote of 3 Pin
David 'dex' Schwartz22-May-12 2:06
David 'dex' Schwartz22-May-12 2:06 
Alex.
I am not convinced the code qualifies as Advanced.
It is very useful to show the two ways of building a <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a>, i.e. add children or set parent.

Keep it simple
dex
AnswerRe: My vote Pin
Volynsky Alex22-May-12 4:13
professionalVolynsky Alex22-May-12 4:13 
GeneralRe: My vote Pin
David 'dex' Schwartz22-May-12 12:58
David 'dex' Schwartz22-May-12 12:58 
GeneralRe: My vote Pin
Volynsky Alex22-May-12 22:33
professionalVolynsky Alex22-May-12 22:33 
GeneralMy vote of 3 Pin
Country Man20-May-12 23:13
Country Man20-May-12 23:13 
QuestionAn example all should follow Pin
jfriedman20-May-12 7:39
jfriedman20-May-12 7:39 
AnswerRe: An example all should follow Pin
Volynsky Alex20-May-12 7:50
professionalVolynsky Alex20-May-12 7:50 
GeneralRe: An example all should follow Pin
jfriedman20-May-12 8:47
jfriedman20-May-12 8:47 
GeneralMy vote of 5 Pin
jfriedman20-May-12 7:37
jfriedman20-May-12 7:37 
GeneralMy vote of 5 Pin
GlebGeglov19-May-12 22:52
GlebGeglov19-May-12 22:52 
GeneralRe: My vote of 5 Pin
Volynsky Alex20-May-12 7:36
professionalVolynsky Alex20-May-12 7:36 
GeneralMy vote of 5 Pin
S.Goldenzwaig17-May-12 12:23
S.Goldenzwaig17-May-12 12:23 
GeneralRe: My vote of 5 Pin
Volynsky Alex17-May-12 12:38
professionalVolynsky Alex17-May-12 12:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.