Post

Observer Pattern

옵저버 패턴에 대해 정리합니다.

Observer Pattern

개요

  • 객체의 상태 변화에 따라 다른 객체에 통지하는 패턴.
  • 주제와 구독자(옵저버) 간의 일대다 관계를 정의한다.
  • 주제는 상태 변화가 발생했을 때 구독자에게 알림을 보낸다.
  • 구독자는 주제의 상태 변화를 감지하고, 필요한 작업을 수행한다.
  • 주제와 구독자는 느슨하게 결합되어 있어, 서로 독립적으로 변경할 수 있다.
  • 주제는 구독자에 대한 정보를 유지하고, 구독자는 주제에 대한 정보를 알지 못한다.
  • 주제는 구독자에게 알림을 보내는 방법을 정의하고, 구독자는 주제의 상태 변화를 감지하는 방법을 정의한다.

사용하는 경우

  • 객체의 상태 변화에 따라 다른 객체에 통지해야 하는 경우
  • 객체 간의 느슨한 결합을 유지하고 싶은 경우
  • 객체의 상태 변화에 따라 다른 객체의 동작을 변경해야 하는 경우

구성요소

  • Subject: 주제를 나타내는 인터페이스
  • ConcreteSubject: 주제를 실제로 구현한 클래스
  • Observer: 옵저버를 나타내는 인터페이스
  • ConcreteObserver: 옵저버를 실제로 구현한 클래스

Observer Pattern

예제

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <functional>
#include <iostream>
#include <list>

class Subject; //Forward declaration for usage in Observer

class Observer
{
public:
    explicit Observer(Subject& subj);
    virtual ~Observer();
  
    Observer(const Observer&) = delete; // rule of three
    Observer& operator=(const Observer&) = delete;

    virtual void update( Subject& s) const = 0;
private:
    // Reference to a Subject object to detach in the destructor
    Subject& subject;
};

// Subject is the base class for event generation
class Subject
{
public:
    using RefObserver = std::reference_wrapper<const Observer>;
  
    // Notify all the attached observers
    void notify()
    {
        for (const auto& x: observers) 
        {
            x.get().update(*this);
        }
    }
  
    // Add an observer
    void attach(const Observer& observer) 
    {
        observers.push_front(observer);
    }
  
    // Remove an observer
    void detach(Observer& observer)
    {
        observers.remove_if( [&observer ](const RefObserver& obj)
        { 
            return &obj.get()==&observer; 
        });
    }
  
private:
    std::list<RefObserver> observers;
};

Observer::Observer(Subject& subj) : subject(subj)
{
    subject.attach(*this);
}

Observer::~Observer()
{
    subject.detach(*this);
}


// Example of usage
class ConcreteObserver: public Observer
{
public:
    ConcreteObserver(Subject& subj) : Observer(subj) {}
  
    // Get notification
    void update(Subject&) const override
    {
        std::cout << "Got a notification" << std::endl;
    }
};

int main() 
{
    Subject cs;
    ConcreteObserver co1(cs);
    ConcreteObserver co2(cs);
    cs.notify();
}

기타

  • 람다를 사용하면 별도 클래스를 만들지 않고도 옵저버를 구현할 수 있다.
  • 푸쉬 방식을 사용하여 주제의 정브를 옵저버에게 전달하는 방식 보다는 풀링 방식으로 옵저버가 주제의 상태를 확인하는 방식이 더 유연하다.
This post is licensed under CC BY 4.0 by the author.