The OBSERVER Pattern


The OBSERVER Pattern

OK, so now that we've been through the example and evolved our code to the OBSERVER pattern, it might be interesting to study what it is. The canonical form of OBSERVER is shown in Figure 32-12. In this example, Clock is being observed by DigitalClock, which registers with the Subject interface of Clock. Whenever the time changes for any reason, Clock calls the Notify method of Subject. The Notify method of Subject invokes the Update method of each registered Observer. Thus, DigitalClock will receive an Update message whenever the time changes, using that opportunity to ask Clock for the time and then display it.

Figure 32-12. Canonical pull-model Observer


OBSERVER is one of those patterns that, once you understand it, you see uses for it everywhere. The indirection is very cool. You can register observers with all kinds of objects rather than writing those objects to explicitly call you. Although this indirection is a useful way to manage dependencies, it can easily be taken to extremes. Overuse of OBSERVER tends to make systems difficult to understand and trace.

Models

The OBSERVER pattern has two primary models. Figure 32-12 shows the pull-model OBSERVER. This model gets its name from the fact that the DigitalClock must pull the time information from the Clock object after receiving the Update message.

The advantage of the pull model is its simplicity of implementation and the fact that the Subject and Observer classes can be standard reusable elements in a library. However, imagine that you are observing an employee record with a thousand fields and that you have just received an Update message. Which of the thousand fields changed?

When Update is called on the ClockObserver, the response is obvious. The ClockObserver needs to pull the time from the Clock and display it. But when Update is called on the EmployeeObserver, the response is not so obvious. We don't know what happened. We don't know what to do. Perhaps the employee's name changed, or maybe the employee's salary changed. Maybe the employee got a new boss. Or maybe the employee's bank account changed. We need help.

This help can be given in the push-model form of the OBSERVER pattern. The structure of the push model is shown in Figure 32-13. Note that the Notify and Update methods both take an argument. The argument is a hint, passed from Employee to SalaryObserver through the Notify and Update methods. That hint tells SalaryObserver what kind of change the Employee record experienced.

Figure 32-13. Push-model Observer


The EmployeeObserverHint argument of Notify and Update might be an enumeration of some kind, a string, or a more complex data structure containing the old and new values of some field. Whatever it is, its value is being pushed toward the observer.

Choosing between the two OBSERVER models is simply a matter of the complexity of the observed object. If the observed object is complex and the observer needs a hint, the push model is appropriate. If the observed object is simple, a pull model will do fine.

Management of OOD Principles

The principle that most drives the OBSERVER pattern is the Open/Closed Principle (OCP). The motivation for using the pattern is so that you can add new observing objects without changing the observed object. Thus, the observed object stays closed.

From Figure 32-12, it should be clear that Clock is substitutable for Subject and that DigitalClock is substitutable for Observer. Thus, the Liskov Substitution Principle (LSP) is applied.

Observer is an abstract class, and the concrete DigitialClock depends on it. The concrete methods of Subject also depend on it. Thus, the Dependency-Inversion Principle (DIP) is applied in this case. You might think that since Subject has no abstract methods, the dependency between Clock and Subject violates DIP. However, Subject is a class that ought never to be instantiated. It makes sense only in the context of a derived class. Thus, Subject is logically abstract, even though it has no abstract methods. We can enforce the abstractness of Subject by giving it a pure virtual destructor in C++ or by making its constructors protected.

There are hints of the Interface Segregation Principle (ISP) in Figure 32-11. The Subject and TimeSource classes segregate the clients of the MockTimeSource, providing specialized interfaces for each of those clients.




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