[Design Pattern] 옵저버 패턴 개요

2015. 4. 18. 05:31프로그래밍/Design Patterns

728x90
728x90

요약

새 소식을 알려줄 수 있는 패턴, JDK에서 가장 많이 쓰이는 패턴, 일대다 관계, 느슨한 결합에 대해 알 것.


비유

출판사 + 구독자 = 옵저버 패턴

출판사를 주제(subject), 구독자를 옵저버(object)라고 부른다는 것을 외워둔다.


주제의 데이터가 바뀌면 새로운 데이터 값이 옵저버 객체에게 전달된다.

옵저버들이 주제의 데이터를 구독하려면 등록 과정을 거쳐야한다.

등록을 하지 않았거나 구독을 중지하면 더 이상 주제로부터 정보를 받지 않는다.


정의

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 정보가 전달되고

자동으로 내용이 갱신되는 일대다 의존성이 정의된 패턴을 말한다.


장점

느슨한 결합도(Loose Coupling)

- 주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스를 구현한다는 것 뿐이다.(옵저버가 뭘 하는진 관심이 없다.)

- 옵저버는 언제든지 새로 추가할 수 있다. 물론 제거도.

- 새로운 형식의 옵저버를 추가하려고 할 때도 주제를 전혀 변경할 필요가 없다.

- 주제와 옵저버는 서로 독립적으로 재사용할 수 있다.

- 주제나 옵저버가 바뀌더라고 서로 영향을 미치지 않는다.


느슨한 결합 디자인은 변경 사항이 생겨도 무난히 처리할 수 있는

유연한 객체지향 시스템을 구축할 수 있고 상호의존성을 최소화 할 수 있다.


push방식과 pull 방식

push방식 - 옵저버가 수동적으로 주제가 주는 정보만을 기다리며 정보를 받는다.

pull방식 - 옵저버가 필요한 시점에 정보를 얻어 목적에 맞게 사용한다.

이는 주제쪽에서 게터 메소드를 만들어 옵저버가 필요한 정보를 가져 갈 수 있게 해주어야 한다.

주제의 정보를 더 드러내야 하지만 옵저버의 종류가 많고 필요한 모든 정보를 파악하기 어렵다.


단점

push방식 - 모든 옵저버가 프로그램에 따라 불필요한 정보를 받을 수 있다.

pull방식 - 필요한 정보만 가져올 수 있으나 주제의 정보가 드러나고 필요한 것이 있을때마다 옵저버 쪽에서 호출해야 하기때문에

메소드를 반복적으로 호출하는 일이 생길 수 있다.


활용

예) 신문구독 시스템, 기상관측 시스템

하나의 객체가 다른 많은 객체에게 정보를 전달해야하는 프로젝트에 유용하게 쓰일 수 있다.


실습 예제

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <iostream>
#include <string>
#include <vector>
#include <forward_list>
using namespace std;
 
//< 상태를 저장하는 이넘
enum { NORMAL, STUN, POISON };
 
//< 옵저버 추상 클래스
class Observer
{
public:
    void update(const int& state)
    {
        m_state = state;
    }
    virtual void response() = 0;
protected:
    int m_state;
};
 
//< 옵저버 추상 클래스로부터 워리어 클래스 생성
class Warrior : public Observer      
{
public:
    virtual void response() override
    {
        switch (m_state)
        {
        case NORMAL:
            cout<<"보스가 정상 상태입니다. 특수 스킬이 비활성화 됩니다."<<endl;
            break;
        case STUN:
            cout<<"워리어"<<endl; 
            cout<<"보스가 스턴 상태입니다. 화이퍼 스매쉬 스킬이 활성화됩니다."<<endl;
            break;
        case POISON:
            break;
        }
    }
};
 
//< 옵저버 추상 클래스로부터 가디언 클래스 생성
class Guardian : public Observer      
{
public:
    virtual void response() override
    {
        switch (m_state)
        {
        case NORMAL:
            cout<<"보스가 정상 상태입니다. 특수 스킬이 비활성화 됩니다."<<endl;
            break;
        case STUN:
            cout<<"가디언"<<endl; 
            cout<<"보스가 스턴 상태입니다. 홀리 바인딩 스킬이 활성화됩니다."<<endl;
            break;
        case POISON:
            break;
        }
    }
};
 
//< 옵저버 추상 클래스로부터 위자드 클래스 생성
class Wizzard : public Observer   
{
public:
    virtual void response() override
    {
switch (m_state)
        {
        case NORMAL:
            cout<<"보스가 정상 상태입니다. 특수 스킬이 비활성화 됩니다."<<endl;
            break;
        case STUN:
            cout<<"위저드"<<endl; 
            cout<<"보스가 스턴 상태입니다.  인헨스드 메테오 스킬이 활성화됩니다."<<endl;
            break;
        case POISON:
            cout<<"위저드"<<endl; 
            cout<<"보스가 중독 상태입니다. 포이즌 키스 스킬이 활성화됩니다."<<endl;
            break;
        }
    }
};
 
//< 옵저버 추상 클래스로부터 프리스트 클래스 생성
class Priest : public Observer      
{
public:
    virtual void response() override
    {
        switch (m_state)
        {
        case NORMAL:
            cout<<"보스가 정상 상태입니다. 특수 스킬이 비활성화 됩니다."<<endl;
            break;
        case STUN:
            cout<<"프리스트"<<endl; 
            cout<<"보스가 스턴 상태입니다.  홀리 라이트 스킬이 활성화됩니다."<<endl;
            break;
        case POISON:
            break;
        }
    }
};
 
//< 주제 추상 클래스 생성
class Subject
{
public:
    virtual void registerObserver(Observer *ob) = 0;
    virtual void removeObserver(Observer *ob) = 0;
    virtual void notifyObserver() = 0;
protected:
    vector<Observer*> m_observers;
};
 
//< 주제 추상 클래스로부터 보스 상태 객체 생성
class BossState : public Subject
{
private:
    int m_state;
 
private:
    void stateChange()
    {
        notifyObserver();
    }
public:
    virtual void registerObserver(Observer *ob) override
    {
        m_observers.push_back(ob);
    }
    virtual void removeObserver(Observer *ob) override
    {
        vector<Observer*>::iterator iter;
        iter = find(m_observers.begin(), m_observers.end(), ob);
 
        m_observers.erase(iter);
    }
    //< 옵저버들에게 정보가 업데이트 되었음을 알리는 함수
    virtual void notifyObserver() override
    {
        vector<Observer*>::iterator iter;
        for (iter = m_observers.begin(); iter != m_observers.end(); iter++)
        {
            (*iter)->update(getState());
        }
    }
 
    //< 정보가 변경되었는지 직접 알아내는 게터
    const int& getState(void)
    {
        return m_state;
    }
 
    void setState(const int state)
    {
        m_state = state;
        stateChange();
    }
};
 
void main()
{
    cout<<"4인팟으로 던전보스 수퍼소니코를 잡으러 입장합니다."<<endl;
    Warrior warriorPlayer;
    Guardian guardianPlayer;
    Wizzard wizzardPlayer;
    Priest priestPlayer;
    
 
    cout<<"던전 보스 수퍼소니코가 등장합니다."<<endl;
    BossState SuperSonico;
 
    cout<<"보스는 주제가되고 유저들은 옵저버가 되어 보스 상태에 대한 정보를 받아본다."<<endl;
    SuperSonico.registerObserver(&warriorPlayer);
    SuperSonico.registerObserver(&guardianPlayer);
    SuperSonico.registerObserver(&wizzardPlayer);
    SuperSonico.registerObserver(&priestPlayer);
 
    cout<<endl<<"보스의 상태가 스턴이 됩니다."<<endl<<endl;
    SuperSonico.setState(STUN);
 
    warriorPlayer.response();
    guardianPlayer.response();
    wizzardPlayer.response();
    priestPlayer.response();
 
    cout<<endl<<"보스의 상태가 중독이 됩니다."<<endl<<endl;
 
    SuperSonico.setState(POISON);
 
    warriorPlayer.response();
    guardianPlayer.response();
    wizzardPlayer.response();
    priestPlayer.response();
 
    cout<<endl<<"보스의 상태가 정상이 됩니다."<<endl<<endl;
 
    SuperSonico.setState(NORMAL);
 
    warriorPlayer.response();
    guardianPlayer.response();
    wizzardPlayer.response();
    priestPlayer.response();
 
    cout<<endl<<"워리어가 사망했습니다. 더 이상 보스의 상태를 받아보지 않습니다."<<endl;
 
    SuperSonico.removeObserver(&warriorPlayer);
    
    cout<<endl<<"보스의 상태가 스턴이 됩니다."<<endl<<endl;
    SuperSonico.setState(STUN);
 
    warriorPlayer.response();
    guardianPlayer.response();
    wizzardPlayer.response();
    priestPlayer.response();
 
}
cs




728x90
반응형

'프로그래밍 > Design Patterns' 카테고리의 다른 글

[Design Pattern] 싱글톤  (2) 2016.02.15
[Design Pattern] 팩토리 패턴  (2) 2016.01.30
디자인 패턴 연관도  (1) 2015.07.08
중재자 패턴  (2) 2015.05.04
싱글톤 사용하기  (2) 2015.04.21
디자인 패턴, 싱글톤, 상태패턴  (2) 2015.04.15