2015. 4. 18. 05:31ㆍ프로그래밍/Design Patterns
요약
새 소식을 알려줄 수 있는 패턴, 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 |
'프로그래밍 > 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 |