Home [Design Patterns] Observer Pattern
Post
Cancel

[Design Patterns] Observer Pattern

Definition

The observer pattern defines a one-to-many dependency between objects. So that when it (the observable) changes its state, all of its dependencies are notified.

Scenario

Instead of making observer keep asking “if there is an update in some data” (polling), we make the system push when itself has changed.

This also helps separating or decoupling methods of objects that trigger an event and methods of objects that should react to the event.

Class Diagram

Observer can subscribe to observable using Add or Remove. When the observable changes, it calls Notify, which loops through a list of subscribed observer and calls their Update method.

Observer pattern Observer pattern

There are two kinds of diagram for pulling data from ConcreteObservable. We can either expose GetData method in ConcreteObservable and let ConcreteObserver pull the data on their own (Method 1) or pass it in Update method (Method 2).

The diagram draws the first method, and the implementation shows the second method.

Method 1 – Make observer has knowledge of observerable

Most of the time, we want to make dependency relation between interfaces not concretion, so this is an exception.

We make ConcreteObserver has a ConcreteObservable, so that it call pull data itself by calling method defined inside ConcreteObservale when being notified by Notify.

Method 2 – Passing data through function

We can simply pass data to Update and get information.

Used Cases

  • In GUI components, they can react to events happening in other objects without coupling to their classes.

Questions

Can observer patterns use for out-of-process database?

Design patterns are about how the code is structured within the program.1 So we might want the observable to watch the database and notify the observer if it changes.

Or we may need service oriented architecture.

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// observe.h
#ifndef __OBSERVE_H__
#define __OBSERVE_H__

#include <vector>

class IObserver {
public:
    virtual void Update(double temperature) = 0;
};

class IObservable {
protected:
    std::vector<IObserver*> observer_list_;
public:
    virtual void Add(IObserver*) = 0;
    virtual void Remove(IObserver*) = 0;
    virtual void Notify() = 0;
};

class WeatherStation : public IObservable {
private:
    double temperature_;

public:
    void Add(IObserver* observer) override;
    void Remove(IObserver* observer) override;
    void Notify() override;
    void GetTemperature();
    void Run();
};

class PhoneDisplay : public IObserver {
public:
    void Update(double temperature) override;
};

class TabletDisplay : public IObserver {
public:
    void Update(double temperature) override;
};

#endif  //__OBSERVE_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// observe.cpp
#include "observe.h"

#include <iostream>

void WeatherStation::Add(IObserver* observer) { observer_list_.emplace_back(observer); }

void WeatherStation::Remove(IObserver* observer) {
    for (unsigned int i = 0; i < observer_list_.size(); i++) {
        if (observer_list_[i] == observer) {
            observer_list_.erase(observer_list_.begin() + i);
        }
    }
}

void WeatherStation::Notify() {
    for (auto o : observer_list_) {
        o->Update(temperature_);
    }
}

void WeatherStation::GetTemperature() { temperature_ = 1.0; }

void WeatherStation::Run() {
    GetTemperature();
    Notify();
}

void PhoneDisplay::Update(double temperature) {
    std::cout << "[PhoneDisplay] Updating temperature = " << temperature << std::endl;
}

void TabletDisplay::Update(double temperature) {
    std::cout << "[TabletDisplay] Updating temperature = " << temperature << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main.cpp
#include "observe.h"

int main() {
    PhoneDisplay* phone_display = new PhoneDisplay();
    TabletDisplay* tablet_display = new TabletDisplay();

    WeatherStation weather_station;
    weather_station.Add(phone_display);
    weather_station.Add(tablet_display);
    weather_station.Run(); // some logic that makes weather station want to push data

    delete phone_display;
    delete tablet_display;

    return 0;
}

Reference

This post is licensed under CC BY 4.0 by the author.