Describe some of the Tradeoffs of implementing the Observer Pattern
Tradeoffs when implementing the Observer Pattern
The prime benefit of the Observer pattern is that objects can vary independent of each other. In particular, you can add or delete different Observer objects as needed. The Observer objects may be of the same or different classes as long as they implement the same Observerinterface.
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of its methods.
The observer pattern is mainly used to implement distributed event handling systems.
Observer is an Interface implemented by Multiple Classes
This is one of those tricky areas where exact details vary a lot from language to language.
In Java, the Observer is almost always an interface implemented by multiple classes.
In C++, the Observer is likely to be an abstract class mixed in through multiple inheritance .
In Smalltalk, the weak typing allows the Observer interface to be merely an abstract idea rather than something directly implemented in code.
Observer Design Pattern
Model the independent functionality with a subject abstraction
Model the dependent functionality with observer hierarchy
The Subject is coupled only to the Observer base class
Observers register themselves with the Subject
The Subject broadcasts events to all registered Observers
Observers pull the information they need from the Subject
Client configures the number and type of Observers
#include <iostream>
#include <vector>
using namespace std;
class Subject {
// 1. "independent" functionality
vector <class Observer * > views; // 3. Coupled only to "interface"
int value;
public:
void attach(Observer *obs) {
views.push_back(obs);
}
void setVal(int val) {
value = val;
notify();
}
int getVal() {
return value;
}
void notify();
};
class Observer {
// 2. "dependent" functionality
Subject *model;
int denom;
public:
Observer(Subject *mod, int div) {
model = mod;
denom = div;
// 4. Observers register themselves with the Subject
model->attach(this);
}
virtual void update() = 0;
protected:
Subject *getSubject() {
return model;
}
int getDivisor() {
return denom;
}
};
void Subject::notify() {
// 5. Publisher broadcasts
for (int i = 0; i < views.size(); i++)
views[i]->update();
}
class DivObserver: public Observer {
public:
DivObserver(Subject *mod, int div): Observer(mod, div){}
void update() {
// 6. "Pull" information of interest
int v = getSubject()->getVal(), d = getDivisor();
cout << v << " div " << d << " is " << v / d << '\n';
}
};
class ModObserver: public Observer {
public:
ModObserver(Subject *mod, int div): Observer(mod, div){}
void update() {
int v = getSubject()->getVal(), d = getDivisor();
cout << v << " mod " << d << " is " << v % d << '\n';
}
};
int main() {
Subject subj;
DivObserver divObs1(&subj, 4); // 7. Client configures the number and
DivObserver divObs2(&subj, 3); // type of Observers
ModObserver modObs3(&subj, 3);
subj.setVal(14);
}
Maintain Consistency between Related Objects
A common side-effect of partitioning a system into a collection of cooperating classes is the need to maintain consistency between related objects. You do not want to achieve consistency by making the classes tightly coupled, because that reduces their reusability. For example, many graphical user interface toolkits separate the presentational aspects of the user interface from the underlying application data.
Classes defining application data and presentations can be reused independently or they can work together.Both a spreadsheet object and bar chart object can depict information in the same application data object using different presentations.
The spreadsheet and the bar chart do not know about each other, thereby letting you reuse only the one you need. But the spreadsheet and the bar chart behave as though they do. When the user changes the information in the spreadsheet, the bar chart reflects the changes immediately, and vice versa.
Observer pattern used in Excel, bar chart, and pie chart.
This behavior implies that the spreadsheet and bar chart are dependent on the data object and therefore should be notified of any change in its state. And there is no reason to limit the number of dependent objects to two; there may be any number of different user interfaces to the same data. The Observer pattern describes how to establish these relationships and the key objects in this pattern are
subject and
observer.
A subject may have any number of dependent observers and all observers are notified whenever the subject under goes a change in state. In response, each observer will query the subject to synchronize its state with the state of the subject. This kind of interaction is also known as publish-subscribe. The subject is the publisher of notifications and sends out these notifications without having to know who its observers are. Any number of observers can subscribe to receive notifications.
Potential Pitfalls of implementing the Observer Pattern
You should be aware of a few potential pitfalls when implementing the Observer pattern:
Generally, you need to make sure that the change of state is complete and that the object has a consistent state before notifying observers of the change.
Be clear about whether the order in which Observer objects are notified is or is not predictable.
Do not let an Observer register itself twice with the same Observed object unless you are sure that is what it means to do.