- 9.1 QObject's Child Managment
- 9.2 Composite Pattern: Parents and Children
- 9.3 QApplication and the Event Loop
- 9.4 Q_OBJECT and moc: A Checklist
- 9.5 Values and Objects
- 9.6 tr() and Internationalization
- Point of Departure
- Review Questions
9.2 Composite Pattern: Parents and Children
According to [Gamma95], the Composite pattern is intended to facilitate building complex (composite) objects from simpler (component) parts by representing the part-whole hierarchies as tree-like structures. This must be done in such a way that clients do not need to distinguish between simple parts and more complex parts that are made up of (i.e., contain) simpler parts.
In Figure 9.1 there are two distinct classes for describing the two roles.
- A composite object is something that can contain children.
- A component object is something that can have a parent.
Figure 9.1 Components and composites
In Figure 9.2, we can see that QObject is both composite and component. We can express the whole-part relationship as a parent-child relationship between QObjects. The highest level (i.e., most "composite") QObject in such a tree (i.e., the root of the tree) 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.
Figure 9.2 QObject
For an example of how this pattern might be used, let's look at Suffolk University. In 1906 the founder, Gleason Archer, decided to start teaching the principles of law to a small group of tradesmen who wanted to become lawyers. He was assisted by one secretary and, after a while, a few instructors. The organizational chart for this new school was quite simple: a single office consisting of several employees with various tasks. As the enterprise grew, the chart gradually became more complex with the addition of new offices and departments. Today, 100 years later, the Law School has been joined with a College of Arts and Sciences, a School of Management, a School of Art and Design, campuses abroad, and many specialized offices so that the organizational chart has become quite complex and promises to become more so. Figure 9.3 shows an abbreviated and simplified subchart of today's Suffolk University.
Figure 9.3 Suffolk University organizational chart
Each box in the chart is a component. It may be composite and have sub-components which, in turn, may be composite or simple components. For example, the PresidentOffice has individual employees (e.g., the President and his assistants) and sub-offices (e.g., DiversityServices). The leaves of this tree are the individual employees of the organization.
We can use the Composite pattern to model this structure. Each node of the tree can be represented by an object of
class OrgUnit : public QObject { public: QString getName(); double getSalary(); private: QString m_Name; double m_Salary; };
The QObject public interface allows us to build up a tree-like representation of the organization with code that instantiates an OrgUnit and then calls setParent() to add it to the appropriate child list.
For each OrgUnit pointer ouptr in the tree, we initialize its m_Salary data member as follows:
- If ouptr points to an individual employee, we use that employee's actual salary.
- Otherwise we initialize it to 0.
We can implement the getSalary() method somewhat like this:
double OrgUnit::getSalary() { QList<OrgUnit*> childlst = findChildren<OrgUnit*>(); double salaryTotal(m_Salary); if(!childlst.isEmpty()) foreach(OrgUnit* ouptr, childlst) salaryTotal += ouptr->getSalary(); return salaryTotal; }
A call to getSalary() from any particular node returns the total salary for the part of the university represented by the subtree whose root is that node. For example, if ouptr points to University, ouptr->getSalary() returns the total salary for the entire university. But if ouptr points to EnglishDpt, then ouptr->getSalary() returns the total salary for the English Department.
9.2.1 Finding Children
QObject provides convenient and powerful functions named findChildren() for finding children in the child list. The signature of one of its overloaded forms looks like this:
QList<T> parentObj.findChildren<T> ( const QString & name ) const
If name is an empty string, findChildren() works as a class filter by returning a QList holding pointers to all children, which can be typecast to type T.
To call the function, you must supply a template parameter after the function name, as shown in Example 9.4.
Example 9.4. src/findchildren/findchildren.cpp
[ . . . . ] /* Filter on Customer* */ QList<Customer*> custlist = parent.findChildren<Customer*>(); foreach (Customer* current, custlist) { qDebug() << current->toString(); } [ . . . . ]