Previous Section  < Free Open Study >  Next Section

4.9 More Containment Heuristics

Heuristic 4.13

A class must know what it contains, but it should not know who contains it.

This heuristic is important if a designer wishes to reuse his or her abstractions. Consider the relationship "BedRoom contains AlarmClock". It is necessary that the BedRoom object knows about its contained AlarmClock object, but the AlarmClock should have no dependencies on the BedRoom. The main reason is reusability. I want to be able to take the AlarmClock out of the BedRoom and use it in the design of a TimeLockSafe. If the AlarmClock is dependent on the BedRoom, then the BedRoom goes wherever the AlarmClock goes. This implies that the Bed, SleepingHuman, NightStand, Bureau, Trunk, etc. also go where the AlarmClock goes since they are contained together in the BedRoom. Objects of the TimeLockSafe class are now dependent on NightStands, Beds, Trunks, etc.

Heuristics 4.14

Objects that share lexical scope梩hose contained in the same containing class梥hould not have uses relationships between them.

Objects that share lexical scope are objects that are contained in the same containing class. The reason they should not be using each other is anchored in both reusability and complexity issues. Consider Figure 4.12's fragment of an automatic teller machine design, which focuses on an ATM attempting to get a pin number from its user. The card reader has detected that it has a legal bank card. The system now needs to get a pin number for verification. A typical solution is for the card reader to ask the ATM's display screen to display a prompt to the user, followed by the card reader asking the keypad to get a pin number.

Figure 4.12. Example of objects sharing lexical scope and using each other.


The problem in this design is initially a reusability issue. Card readers have nothing to do with display screens and keypads outside of the domain of automatic teller machines. If I want to reuse the card reader in a security door, I have to place a display screen and keypad in the door because card readers are dependent on them. This clearly hinders the reuse of the class sending the messages.

Even if a developer decides that he or she does not care about reusability, there is a serious complexity issue that needs resolution. The automatic teller machine contains all three objects, which implies that there are uses relationships between the ATM and card reader, the ATM and display screen, and the ATM and keypad. The ATM can handle any coordinated activity between these three classes. By adding a uses relationship between the card reader and display screen, we are adding complexity to the design whose functionality is already available in the ATM machine. A better solution is to allow the ATM to find that it has a card, prompt the user via its display screen, and get a pin number from its keypad (see Figure 4.13). In general, the containing class should use its existing uses relationships to accomplish the behavior that the lexical scope sharing objects are duplicating.

Figure 4.13. A better solution to the objects sharing lexical scope problem.


There is a notable area of design where a designer will violate Heuristic 4.13. This occurs when a number of different classes are dependent on one another in complex ways. Rather than have all of these complex dependencies, the designer wraps the related classes in a containing class. Each contained object sends messages to the containing class, which then broadcasts the message to the rest of the contained objects or a preselected list, depending on how the designer wants to distribute the system intelligence between the containing class and its contained pieces. In all of these designs, the designer is violating the heuristic in order to satisfy Heuristics 4.1 and 4.14. In short, having a contained object that knows about its container is preferable to a large number of uses relationships. Three case studies illustrate this violation of Heuristic 4.13:

The first example can be found in the domain of the securities industry. In a particular securities trading application, the designer found himself with a number of different types of objects that fell under three categories: price providers (databases, real-time pricing information, economic models), markets (U.S. Treasuries Market, Madrid Stock Exchange, New York Stock Exchange), and analyzers (domain-defined or homemade analytics for securities). Objects of these classes are all dependent on one another, in that, if one changes it could have an impact on any of the other objects. Rather than creating a uses relationship from each of the classes to each of the others, the designer chose to wrap all of the objects in a new containing class which he called an economy. If any of the contained objects changed state, they could inform the economy, which propagated the changes to each of the other contained objects. They, in turn, could produce further changes, of which the economy object could be informed. This design proved extremely flexible and substantially less complex had the designer elected to enforce Heuristic 4.13.

A second example of a proper violation of Heuristic 4.13 can be found in the domain of telephony. A development team was implementing a maintenance application for telephone service hardware. In such a domain, there are many devices which the development team named features. There are many different types of features, some of which can be dependent on each other. Rather than have many different features sending messages to each other, or even knowing whether the other class was available, all message sends were to a containing class called the unit. The unit class was responsible for reducing the coupling between its contained pieces, by mapping requests between the sender and receiver. It could also handle the complexity of deciding whether or not the desired entity was available.

The third case is the widely published dialog box example. In graphical user interfaces, dialog boxes are often viewed as collections of objects of a number of different types. These different objects often communicate with each other to achieve some goal. Rather than have each contained object know about the existence of the other contained objects, we allow the dialog box to handle the communication. As in the two previous domains, each contained object sends messages to the containing class (in this case the dialog box class) and the containing class broadcasts the request to its pieces.

    Previous Section  < Free Open Study >  Next Section