The Observer Pattern was defined by the Gang of Four (GoF). It is also known as Dependents-, and Publish-Subscribe-Pattern.
Motivation
The intention for using the observer pattern is to have a one-to-many dependency between objects. When one object changes it’s state, all the other objects shall be notified. This makes it possible to couple different modules with each other without the need of hardcoding their dependency. Thus, you get better structured, more reusable classes.
Example
Consider a spreadsheet application, that can illustrate the data with different diagrams. By using the Observer-Pattern, every diagram-object would be an observer, and every spreadsheet-object would be a subject. The observers and the subjects are coupled with each other, while every subject can be coupled with many observers. If the user changes something in a diagram, the diagram-object would request the modification at the spreadsheet-object. Now, the spreadsheet-object can start calculating. When it finished, all the diagram-objects are notified, and they can update their view with the modified data.
When to use it
- When a class has two aspects, and one is dependent on the other. By separating these aspects into observer and subject, you can reuse them independently
- When modifying one object requires to modify other objects, and you don’t know how many
- When you don’t want to couple objects tightly with each other
Structure
- ConcreteObserver
- Implements the update() method, inherited by the Observer Interface
- Synchronizes it’s subjectState with the observerState
- Has a reference to it’s ConcreteSubject, to call methods like getState() or setState(state)
- Observer
- Interface with an abstract method – update(), that is called, when the Subject notifies the Observer
- ConcreteSubject
- Stores the subjectState
- Notifies it’s Observers, when the subjectState changed
- Subject
- Interface for attaching and detaching Observers
- Hold’s a reference to it’s Observers
Interaction
Implementation
The code can be downloaded here. Observer.h
class Observer {
public:
virtual void update() = 0;
};
ConcreteObserver.h
#include "ConcreteSubject.h"
#include <string>
class ConcreteObserver: public Observer {
private:
std::string observerState;
ConcreteSubject* subject;
public:
ConcreteObserver(std::string state, ConcreteSubject* subject);
void modifyTheSubject(std::string state);
std::string getObserverState();
void update();
};
ConcreteObserver.cpp
#include "ConcreteSubject.h"
std::string ConcreteSubject::getState() {
return subjectState;
}
void ConcreteSubject::setState(std::string state) {
this->subjectState = state;
notify();
}
Subject.h
#include <list>
#include "Observer.h"
class Subject {
private:
std::list <Observer*> observers;
public:
void attachObserver(Observer* observer);
void detachObserver(Observer* observer);
void notify();
};
Subject.cpp
#include "Subject.h"
void Subject::attachObserver(Observer* observer) {
observers.push_back(observer);
}
void Subject::detachObserver(Observer* observer) {
for (std::list<Observer*>::iterator it = observers.begin(); it != observers.end(); it++) {
if (observer == *it) {
observers.erase(it);
break;
}
}
}
void Subject::notify() {
for (auto obs : observers) {
obs->update();
}
}
ConcreteSubject.h
#include <string> #include "Subject.h" class ConcreteSubject: public Subject { private: std::string subjectState; public: std::string getState(); void setState(std::string state); };
ConcreteSubject.cpp
#include "ConcreteSubject.h"
std::string ConcreteSubject::getState() {
return subjectState;
}
void ConcreteSubject::setState(std::string state) {
this->subjectState = state;
notify();
}
…Finally for testing main.cpp
#include <iostream>
#include "ConcreteObserver.h"
int main(int argc, const char * argv[]) {
auto subj = ConcreteSubject();
auto obs1 = ConcreteObserver("observer 1 hasn't changed", &subj);
auto obs2 = ConcreteObserver("observer 2 hasn't changed", &subj);
subj.attachObserver(&obs1);
subj.attachObserver(&obs2);
std::cout << "observer 1: " << obs1.getObserverState() << "\n";
std::cout << "observer 2: " << obs2.getObserverState() << "\n";
obs1.modifyTheSubject("All observers changed \n");
std::cout << "observer 1: " << obs1.getObserverState();
std::cout << "observer 2: " << obs2.getObserverState();
return 0;
}
Source: Gang of Four – Design Patterns. Elements of Reusable Object-Oriented Software