![]() |
|||||||||||||||||||||||||
4.6 More Complex Static Aggregation |
|||||||||||||||||||||||||
| In this section, the StopWatch class is implemented to illustrate
a more complex aggregation, one in which the aggregation subobjects
are more functional and in which the problems of properly constructing
the aggregation are more challenging. Before considering the issue
of how to construct this more elaborate aggregation, a general
design issue is described, involving the degree to which the internal
subobjects of the aggregation are implicitly revealed through
the aggregation's public interface. This is termed the problem of indirect control.
As can be seen, these questions touch on all of the encapsulated subobjects in the StopWatch class. Ideally, no indirect control over the encapsulated objects should be allowed, but this may not always be reasonable or possible. Beware, though, that providing excessive indirect control over the encapsulated objects begins to weaken the advantages of aggregation. In the extreme case, the aggregating class relinquishes complete control of its subobjects, becoming little more than a weak wrapper to hold the subobjects together as a group. The designer of a good class must strike a balance between providing sufficient indirect control of the encapsulated objects so as to be usable in different applications, yet not so much as to lose the benefits of aggregation. The designer of an aggregating class has several options for dealing with the issue of subobject control. First, several similar classes can be designed, each providing a different degree of control. At the expense of creating and naming several classes, this approach allows a spectrum of choices for programmers who may have varying needs for control over the subobjects. Second, the interface of the aggregating class may use default arguments so that control reverts to the aggregating class itself if these arguments are not specified. While this approach also promises a spectrum of choices, the argument lists become more complicated and more difficult to design. Due to the ordering of the default arguments, a user of the aggregating class may find it necessary to assume more control than is desired. Third, one or more auxiliary classes can be defined for specifying the control information. An instance of this auxiliary class is provided as an argument to the aggregating class. This argument may also have a default value, or the auxiliary class may have a default constructor so that users may assume no indirect control. In the case of the StopWatch class, the arrangement of the user interface components can be collected into a StopWatchLayout class, which would contain all of the placement and labeling information for a StopWatch. The default StopWatchLayout constructor would provide standard (default) values for this information. The constructor for the StopWatch class would have a StopWatchLayout argument whose default value is a StopWatchLayout object constructed by the default constructor of the StopWatchLayout class. Implementing the StopWatch Class The definition of the StopWatch class is given below. The public interface provides methods to start and stop the StopWatch from the program level, to identify a Frame in which the StopWatch can display its user interface components (TextBox and Buttons), and to query the StopWatch for its current elapsed time.
Notice that this design makes a number of decisions about the control issues raised earlier. This is not to suggest that these are the right decisions, they are only reasonable and illustrative ones. The methods of the StopWatch class can be implemented as follows:
This code illustrate how the methods of the StopWatch class achieve their effect by manipulating the internal subobjects. For example, for the StopWatch to start or stop itself in response to one of its buttons being pressed, the ButtonPushed method simply calls the Start() or Stop() method of the Clock subobject. Similarly, the elapsed time of the StopWatch is found simply by querying the value of the Counter subobject. The constructor for the StopWatch class was not included above, but will be shown in the next section, where the concept of constructing subobjects is considered separately. Constructing Subobjects The constructor of an aggregating class must insure that its subobjects are properly initialized. It is expected, for example, that when a StopWatch object is constructed, all of its subobjects are also properly constructed and ready for use. The constructor for a subobject may be related to the constructor of the aggregating class in one of three ways:
These relationships are shown pictorially in the following figure.
In the independent case, the subobject has a fixed constructor that does not depend in any way on the construction of the aggregating class. For example, in the StopWatch design, the Counter subobject is always initialized to zero regardless of any other properties of the StopWatch object being constructed. Also the Start button, the Stop button, and the Message are constructed without reference to the StopWatch constructor values. The direct case is illustrated by the Clock subobject in the StopWatch class. Here, the Clock object's constructor argument, the timer interval, is taken exactly from the StopWatch constructor argument. No change is made in this value. The indirect case is illustrated by the construction of the Canvas and Panel. The StopWatch constructor has a single Location argument relative to which these two user interface objects are positioned. Assume that the user interface subobjects are positioned as shown in the following figure:
Using the layout picture above, the locations of the user-interface subobjects would be determined as shown in the table below. The computation of the subobject locations involves invoking methods (Xcoord and Ycoord) of the StopWatch constructor argument (where), performing simple addition, and constructing a new (anonymous) Location object.
In C++, the subobject constructors are placed as shown in this general form:
ClassName::ClassName( <argument list> ) : <subobject constructor list>
{ <body of constructor> }
where <subobject constructor list> is a comma-separated list of constructors for subobjects. This list may contain only constructors for subobjects. Using the layout decisions made above and the C++ syntax just introduced, the constructor for the StopWatch class can now be written as follows.
Tasks
|
|||||||||||||||||||||||||
|
|||||||||||||||||||||||||
ÿ