Click here to Skip to main content
Click here to Skip to main content

Tagged as

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

, 17 May 2012
Rate this:
Please Sign up or sign in to vote.
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:

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;
    
    //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...

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

        //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:

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)

Share

About the Author

Volynsky Alex
Software Developer
Israel Israel
Mr.Volynsky Alex is a Software Engineer in a leading software company. Alex is skilled in many areas of computer science. He has over 13 years of experience in the design & development of applications using C/C++/STL, Qt, MFC, COM/ActiveX, DirectShow, JavaScript, VBScript, Tcl/Tk and of course - C#/.NET.
 
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

 
Questioncomponent class should not be a abstract class (method should not be a pure virtual function) PinmemberMember 1007345323-May-13 6:50 
SuggestionRe: component class should not be a abstract class (method should not be a pure virtual function) PinmemberVolynsky Alex23-May-13 9:58 
GeneralMy vote of 5 Pinmembermk4you713-Jun-12 4:54 
GeneralRe: My vote of 5 PinmemberVolynsky Alex14-Jun-12 15:09 
GeneralMy vote of 5 PinmemberSteph_Iv28-May-12 10:32 
GeneralRe: My vote of 5 PinmemberVolynsky Alex14-Jun-12 15:09 
Thanks!
ale44ko

GeneralMy vote of 5 PinmemberEMogilevsky26-May-12 4:28 
GeneralRe: My vote of 5 PinmemberVolynsky Alex14-Jun-12 15:10 
GeneralMy vote of 5 PinmemberGerard Forestier24-May-12 22:15 
QuestionMy vote of 3 PinmemberDavid 'dex' Schwartz22-May-12 2:06 
AnswerRe: My vote PinmemberVolynsky Alex22-May-12 4:13 
GeneralRe: My vote PinmemberDavid 'dex' Schwartz22-May-12 12:58 
GeneralRe: My vote PinmemberVolynsky Alex22-May-12 22:33 
GeneralMy vote of 3 PinmemberCountry Man20-May-12 23:13 
QuestionAn example all should follow Pinmemberjfriedman20-May-12 7:39 
AnswerRe: An example all should follow PinmemberVolynsky Alex20-May-12 7:50 
GeneralRe: An example all should follow Pinmemberjfriedman20-May-12 8:47 
GeneralMy vote of 5 Pinmemberjfriedman20-May-12 7:37 
GeneralMy vote of 5 PinmemberGlebGeglov19-May-12 22:52 
GeneralRe: My vote of 5 PinmemberVolynsky Alex20-May-12 7:36 
GeneralMy vote of 5 PinmemberS.Goldenzwaig17-May-12 12:23 
GeneralRe: My vote of 5 PinmemberVolynsky Alex17-May-12 12:38 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 17 May 2012
Article Copyright 2012 by Volynsky Alex
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid