The Observer design pattern allows an object (often called Subject) to inform other objects (Observers) that an event has occurred.
It's particularly useful when you have data from a Subject but you don't know how this data will be used by the Observers.
For example, data from a Subject can be used to display a text in an Observer and a chart in another Observer (even if data is the same).
You can retrieve this example on my GitHub.
First of all
The abstract classes
We have to create two interfaces: ISubject and IObserver.
The ISubject uses three abstract methods:
- attach(IObserver *);
- detach(IObserver *);
- notify().
As you can see, the attach() and detach() methods have both, as parameter, a pointer to an IObserver.
Then the IObserver has only one abstract method:
In this case we have a string as parameter but it could be antyhing else such as an int or even several parameters of any kind.
In the Observer pattern ISubject is composed of an IObserver.
The concrete classes
The Observer class inherits from IObserver and the Subject from ISubject.
But we add another relation because the Observer is composed of a Subject.
The Subject is responsible to create messages, attach and detach Observers from its Observers list and to notify them all that a new message has arrived.
As this list is composed of abstract types, the Subject doesn't know exactly what kind of concrete objects they are.
It's thus easy to add new kind of Observers to this list.
The Subject notifies the Observers after creating a new message.
The Observer uses the Subject instance to attach itself to the Subject and to detach itself from it.
The Observer receives the message from the Subject with the update() method.
Main
In the main.cpp file I created a Subject and many Observers.
Then I created new messages from the Subject and removed some Observers in order to show how this pattern works.
Code
IObserver.h
#ifndef __BADPROG_IOBSERVER_H__
#define __BADPROG_IOBSERVER_H__
#include <string>
// Badprog.com
using namespace std;
class IObserver {
public:
virtual ~IObserver() {};
virtual void update(const string &messageFromSubject) = 0; // VP
};
#endif // __BADPROG_IOBSERVER_H__
ISubject.h
#ifndef __BADPROG_ISUBJECT_H__
#define __BADPROG_ISUBJECT_H__
#include "IObserver.h"
// Badprog.com
class ISubject {
public:
virtual ~ISubject() {};
virtual void attach(IObserver *observer) = 0; // VP
virtual void detach(IObserver *observer) = 0; // VP
virtual void notify() = 0; // VP
};
#endif // __BADPROG_ISUBJECT_H__
main.cpp
#include "Observer.h"
#include "Subject.h"
// Badprog.com
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
int main()
{
Subject *subject = new Subject();
Observer *observer1 = new Observer(*subject); // new observer
Observer *observer2 = new Observer(*subject); // new observer
Observer *observer3 = new Observer(*subject); // new observer
Observer *observer4;
Observer *observer5;
subject->createMessage("Hello World! :D"); // new message
observer3->removeMeFromTheList(); // removing an observer
subject->createMessage("The weather is hot today! :p"); // new message
observer4 = new Observer(*subject); // new observer
observer2->removeMeFromTheList(); // removing an observer
observer5 = new Observer(*subject); // new observer
subject->createMessage("My new car is great! ;)"); // new message
observer5->removeMeFromTheList(); // removing an observer
observer4->removeMeFromTheList(); // removing an observer
observer1->removeMeFromTheList(); // removing an observer
// deleting
delete observer5;
delete observer4;
delete observer3;
delete observer2;
delete observer1;
delete subject;
return 0;
}
Observer.cpp
#include "Observer.h"
#include <iostream>
// Badprog.com
using namespace std;
int Observer::_staticNumber = 0;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
Observer::Observer(Subject &subject) : _subject(subject) {
_subject.attach(this);
cout << "Hi, I'm the Observer \""
<< ++Observer::_staticNumber << "\"." << endl;
_number = Observer::_staticNumber; // increased each time a new observer is created
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
Observer::~Observer() {
cout << "Goodbye, I was the Observer \"" << _number << "\"." << endl;
}
//-----------------------------------------------------------------------------
// Removes the observer from the observer list.
//-----------------------------------------------------------------------------
void Observer::removeMeFromTheList() {
_subject.detach(this);
cout << "Observer \"" << _number << "\" removed from the list." << endl;
}
//-----------------------------------------------------------------------------
// Retrieves the new message from the subject.
//-----------------------------------------------------------------------------
void Observer::update(const string &messageFromSubject) {
_messageFromSubject = messageFromSubject;
printInfo();
}
//-----------------------------------------------------------------------------
// Displays a message to alert that a new message has arrived.
//-----------------------------------------------------------------------------
void Observer::printInfo() {
cout << "Observer \"" << Observer::_number
<< "\": a new message is available --> "
<< _messageFromSubject << endl;
}
Observer.h
#ifndef __BADPROG_OBSERVER_H__
#define __BADPROG_OBSERVER_H__
#include "IObserver.h"
#include "Subject.h"
// Badprog.com
class Observer : public IObserver
{
public:
Observer(Subject &subject);
virtual ~Observer();
void update(const string &messageFromSubject);
void removeMeFromTheList();
void printInfo();
private:
string _messageFromSubject;
Subject& _subject;
static int _staticNumber;
int _number;
};
#endif // __BADPROG_OBSERVER_H__
Subject.cpp
#include "Subject.h"
#include <iostream>
// Badprog.com
using namespace std;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
Subject::Subject() {
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
Subject::~Subject() {
cout << "Goodbye, I was the Subject." << endl;
}
//-----------------------------------------------------------------------------
// Creates a new message and notifies all observers.
//-----------------------------------------------------------------------------
void Subject::createMessage(string message) {
_message = message;
notify();
}
//-----------------------------------------------------------------------------
// Displays how many observers there are in the list.
//-----------------------------------------------------------------------------
void Subject::howManyObserver() {
cout << "There are "
<< _listObserver.size()
<< " observers in the list." << endl;
}
//-----------------------------------------------------------------------------
// Attaches a new observer in the list.
//-----------------------------------------------------------------------------
void Subject::attach(IObserver *observer) {
_listObserver.push_back(observer);
}
//-----------------------------------------------------------------------------
// Detaches an observer from the list.
//-----------------------------------------------------------------------------
void Subject::detach(IObserver *observer) {
_listObserver.remove(observer);
}
//-----------------------------------------------------------------------------
// Notifies all observers that a new message has arrived.
//-----------------------------------------------------------------------------
void Subject::notify() {
list<IObserver *>::iterator iterator = _listObserver.begin();
howManyObserver();
while (iterator != _listObserver.end()) {
(*iterator)->update(_message);
++iterator;
}
}
Subject.h
#ifndef __BADPROG_SUBJECT_H__
#define __BADPROG_SUBJECT_H__
#include <list>
#include "ISubject.h"
// Badprog.com
class Subject : public ISubject
{
public:
Subject();
virtual ~Subject();
void attach(IObserver *observer);
void detach(IObserver *observer);
void notify();
void createMessage(string message = "Empty");
void howManyObserver();
private:
list<IObserver *> _listObserver;
string _message;
};
#endif // __BADPROG_SUBJECT_H__
Compiling, linking and executing
g++ *.cpp -o go.exe ; ./go.exe
Result
Hi, I'm the Observer "1".
Hi, I'm the Observer "2".
Hi, I'm the Observer "3".
There are 3 observers in the list.
Observer "1": a new message is available --> Hello World! :D
Observer "2": a new message is available --> Hello World! :D
Observer "3": a new message is available --> Hello World! :D
Observer "3" removed from the list.
There are 2 observers in the list.
Observer "1": a new message is available --> The weather is hot today! :p
Observer "2": a new message is available --> The weather is hot today! :p
Hi, I'm the Observer "4".
Observer "2" removed from the list.
Hi, I'm the Observer "5".
There are 3 observers in the list.
Observer "1": a new message is available --> My new car is great! ;)
Observer "4": a new message is available --> My new car is great! ;)
Observer "5": a new message is available --> My new car is great! ;)
Observer "5" removed from the list.
Observer "4" removed from the list.
Observer "1" removed from the list.
Goodbye, I was the Observer "5".
Goodbye, I was the Observer "4".
Goodbye, I was the Observer "3".
Goodbye, I was the Observer "2".
Goodbye, I was the Observer "1".
Goodbye, I was the Subject.
Conclusion
The Observer design pattern is quite interesting because we can manipulate Subject and Observer independently from each other.
The interfaces helped us to achieve this.
If you understood this design pattern, great job you did it!
Add new comment