Class Interfaces versus Object Interfaces


Consider the TimedDoor again. Here is an object that has two separate interfaces used by two separate clients: Timer and the users of Door. These two interfaces must be implemented in the same object, since the implementation of both interfaces manipulates the same data. How can we conform to ISP? How can we separate the interfaces when they must remain together?

The answer lies in the fact that clients of an object do not need to access it through the interface of the object. Rather, they can access it through delegation or through a base class of the object.

Separation Through Delegation

One solution is to create an object that derives from TimerClient and delegates to the TimedDoor. Figure 12-2 shows this solution. When it wants to register a timeout request with the Timer, the TimedDoor creates a DoorTimerAdapter and registers it with the Timer. When the Timer sends the TimeOut message to the DoorTimerAdapter, the DoorTimerAdapter delegates the message back to the TimedDoor.

Figure 12-2. Door timer adapter


This solution conforms to ISP and prevents the coupling of Door clients to Timer. Even if the change to Timer shown in Listing 12-3 were to be made, none of the users of Door would be affected. Moreover, TimedDoor does not have to have the exact same interface as TimerClient. The DoorTimerAdapter can translate the TimerClient interface into the TimedDoor interface. Thus, this is a very general-purpose solution. (See Listing 12-4.)

Listing 12-4. TimedDoor.cs

public interface TimedDoor : Door {   void DoorTimeOut(int timeOutId); } public class DoorTimerAdapter : TimerClient {   private TimedDoor timedDoor;   public DoorTimerAdapter(TimedDoor theDoor)   {     timedDoor = theDoor;   }   public virtual void TimeOut(int timeOutId)   {     timedDoor.DoorTimeOut(timeOutId);   } }

However, this solution is also somewhat inelegant. It involves the creation of a new object every time we wish to register a timeout. Moreover, the delegation requires a very small, but still nonzero, amount of runtime and memory. In some application domains, such as embedded real-time control systems, runtime and memory are scarce enough to make this a concern.

Separation Through Multiple Inheritance

Figure 12-3 and Listing 12-5 show how multiple inheritance can be used to achieve ISP. In this model, TimedDoor inherits from both Door and TimerClient. Although clients of both base classes can make use of TimedDoor, neither depends on the TimedDoor class. Thus, they use the same object through separate interfaces.

Figure 12-3. Multiply inherited TimedDoor


Listing 12-5. TimedDoor.cpp

public interface TimedDoor : Door, TimerClient { }

This solution is my normal preference. The only time I would choose the solution in Figure 12-2 over that in Figure 12-3 is if the translation performed by the DoorTimerAdapter object were necessary or if different translations were needed at different times.




Agile Principles, Patterns, and Practices in C#
Agile Principles, Patterns, and Practices in C#
ISBN: 0131857258
EAN: 2147483647
Year: 2006
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net