4.2 Implementing a New Class Using Aggregation
The Concept of Aggregation
Aggregation is related to, but distinct from, association. The difference between aggregation and association is illustrated in the following figures that show the objects making up a timer system developed earlier. As shown in the following figure, creating the timer system by association involves creating the Clock, Counter, Message, Panel, Canvas, and Button objects individually, and using their methods to build up the desired structure of connections among them.
In this figure, the solid-line arrows show objects connected by pointers. The Counter object, for example, maintains a pointer to the Message object to which it communicates its current value. The dotted-line arrows in the figure represent the relationship between a Frame object and the user-interface components that are displayed in that Frame.
The next figure shows how the same system would appear when aggregation is used. With aggregation, the basic machinery of the timer system is encapsulated inside another object whose class, StopWatch, will be developed throughout this section. The public interface of the StopWatch class provides methods that allow the timer system to be manipulated as a whole.
A given class, for example the Clock class, may be used in several different aggregations. For example, a different timer system might not need user-control buttons. Such a system, a SimpleTimer, could be built using aggregation as shown in the following figure.
Another timer system might be used only for the measurement of time relevant to the program and would not be displayed to the user. This timer system, an InternalTimer, could be organized as shown in the following figure.
Similarly, there might be a variety of systems that aggregate other combinations of these and other classes.
Advantages of Aggregation
Aggregation confers several advantages most of which flow from the use of encapsulation and they are important to recognize and reiterate:
It is clear from these advantages that developing skill in defining and implementing aggregations is an important goal.
Types of Aggregation
Two types of aggregation are described: static aggregation and dynamic aggregation. In static aggregation, the lifetimes of the sub-objects are identical to the lifetimes of the containing object. The sub-objects are explicitly declared in the class of the containing object, they are constructed when the containing object is constructed, and they are destructed when the containing object is destructed. In dynamic aggregation, at least some of the objects known only to the containing object are created dynamically, via the new operator, at run-time.
An intuitive example that illustrates the difference between static and dynamic aggregations is the contrast between an automobile, which has a fixed number of tires, and a tire store, where tires arrive from the factory and are sold to customers. For the automobile, the number of tires is known in advance. However, for the tire store, the type of its contents (i.e., automobile tires) is known, but the number of tires at any one time is variable and cannot be determined in advance.
Static aggregations are usually simpler, safer, and more efficient for a system to manage and thus should be used whenever possible. However, dynamic aggregations are needed for those equally important cases where the type, but not the number, of sub-objects is known at design time. Both forms of aggregation are useful, and a good designer must be able to distingusih between them and use whichever is appropriate for the problem at hand.
Defining a New Class
Aggregation, whether static or dynamic, inherently involves defining a new class. An object of the new class is the outer (encapsulating, whole) component of the aggregation. The sub-objects (the parts) are declared in the private data of the new class. To create high-quality aggregations it is necessary to be proficient in defining and implementing new classes.
A good class definition and implementation must have a number of desirable properites. Because a class captures an abstraction, the class must have all of the properties of a good abstraction. But as an executable representation a class must also have three major propeties beyond those of an abstraction:
Given the desired properites of abstractions and those of a class, it is clear that good object-oriented design is a creative and challenging activity.
The implementor of classes must be a proficient user of basic tools. In addition to the obvious need for a compiler is the need for proficiency in using a symbolic debugger during development and testing. Any system beyond the most trivial ones also requires the use of tools to (re)build the executable system from its source code when some part of that code has changed. For trivial systems all of the code can be recompiled every time any part of it is changed. But with modest and large systems, this brute-force approach is impractical because the time to recompile and relink the system is excessive. However, it is not practical for the implementor to remember all of the ways in which parts of the system must be rebuilt when some parts have changed. Thus, tools must be used to make the (re)building process efficient and accurate.