|< Free Open Study >|
5.11 An Inheritance Solution to an Interrupt-Driven Architecture
Recall Heuristic 4.13 on the containment relationship. It stated that a contained object should not have knowledge of the class that contains it. We stated that this heuristic is important if a designer wishes to reuse his or her abstractions. If the contained object knows its containing class, then it cannot be reused in a domain that does not have the container and everything else in that container.
Following this heuristic often becomes a problem when dealing with interrupt driven devices. Consider the problem where ATM contains a CardReader. A use case of this system states that the user puts a card in a CardReader, which activates the ATM. The most intuitive design for such a system might look like the diagram in Figure 5.25. The main problem with this design is that CardReader cannot be reused outside the domain of the ATM. What if we wish to build a new class called SecurityDoor which contains a CardReader? This design would not allow it.
A better solution is to use the inheritance relationship to generalize the ATM to some device (see Figure 5.26). This uses the inheritance relationship to state that an ATM is a special type of device, and it uses the containment relationship to state that CardReaders are contained in some device, not necessarily an ATM. This reduces the constraint from "CardReaders must be used inside an ATM" to "CardReaders must be used inside some device, of which ATM is one of many." This solution implies that multiple inheritance might be necessary. If the ATM contains two such interrupting devices and wishes to use this generalizing solution, then it will need to inherit from two abstract classes. This inheritance will be easy to live with since the abstract classes will contain only a pure polymorphic interface closely coupled with the contained data object (in this case, the CardReader).
An even better design (from the viewpoint of logical design), which removes all constraints, is to modify the way we look at interrupt-driven systems. In one design course, a participant said, "Everything is a polling system. The difference between polling and interrupt-driven is your point of view. When we poll in hardware, we like to call it interrupt-driven." With this in mind, let us think of the CardReader as a polled device. Even if it is physically interrupt-driven, we can use the interrupt to change the state of the CardReader object. The ATM can poll for a change in this state. This design allows any object or class to ask the CardReader if it has a card (see Figure 5.27). When it has a card, the client object can react any way it likes. This design offers the needed flexibility when dealing with containment hierarchies. The reader should note that this design might not be practical for physical design reasons, such as efficiency. The fact that an ATM does not do anything while waiting for a card allows us to use the polling solution.
|< Free Open Study >|