[GoF] 관찰자 (Observer) 패턴 행동 패턴
객체가 다른 객체의 상태 변화를 구독할 수 있게 하며, 구독한 객체의 상태가 갱신될 경우 객체에게 통지하여 갱신된 상태를 활용할 수 있도록 합니다.
|
요즈음 다양한 게임의 유저 인터페이스(UI)를 들여다보면, 게임 내부의 상태가 변함에 따라 유저 인터페이스도 자동으로 변하는 모습을 볼 수 있다. 예를 들어, 게임이 시작된다면 게임 진행을 위한 메뉴가 보여진다던가, 플레이어의 체력이 감소하면 HP 바가 덩달아 줄어드는 등. 이 두 개념은 서로 거리가 멀어 보이는데, 유저 인터페이스는 어떻게 상태 변화를 감지하고 대응하는것인가? 대부분의 경우 관찰자 패턴을 사용하여 구현할 수 있다.
관찰자 (Observer) 패턴은 관찰하고자 하는 객체에 상태가 변경 될 경우 메소드를 통해 통지받을 수 있도록 구독하는 기능을 지원한다. 다양한 관찰자가 대상을 구독하거나 해지할 수 있으며, 객체의 갱신에 대한 동적인 처리를 수행할 수 있도록 해 준다.
관찰자 패턴은 상태가 변할 수 있는 주체인 대상(Subject) 객체와 그 객체를 구독하여 통지받을 수 있도록 하는 관찰자(Observer) 객체로 구성된다. 대상 객체는 구독된 관찰자 객체 여러개를 참조하기에 서로 일대다(1 : N) 의 관계가 성립된다. 대상 객체와 관찰자 객체는 서로 추상화된 결합만 존재하기에 옵저버 패턴을 이용하면 관찰자 객체를 추가하거나 변경해도 대상 객체를 따로 변경할 필요가 없다. 그러므로, 상태를 활용하려는 객체와 그 상태를 가진 대상과의 결합을 완화시킬 수 있다.
대상 객체의 변경 시, 대상 객체가 관찰자 객체에게 갱신된 상태를 전달하는데 있어 관찰자 패턴은 두 가지 경우로 나누어진다: 대상 객체가 관찰자 객체에게 갱신된 상태를 메소드의 인자를 통해 전달하는 푸시 (Push) 방식과, 관찰자 객체가 대상 객체로부터 갱신된 상태를 직접 가져오는 풀 (Pull) 방식이 있다.
C#에서는 event 라는 키워드를 통해 관찰자 패턴을 자체적으로 이용할 수 있다. event 를 통해 상태의 갱신을 통지받으려는 경우에, 지정된 형식의 함수 포인터를 += 연산자를 통해 추가함으로써 구독의 기능을 수행할 수 있다. 또한, event 로의 구독을 해지하려는 경우 -= 연산자를 이용할 수 있다. event 가 클라이언트로부터 호출 될 경우, event 를 구독한 모든 함수 포인터가 따라서 호출된다.
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 | public class PatternObserver { public abstract class Subject<T> { private List<Observer<T>> _observers; public Subject() { _observers = new List<Observer<T>>(); } public void AttachObserver(Observer<T> observer) { _observers.Add(observer); } public void Notify(T message) { foreach (Observer<T> observer in _observers) { observer.Update(message); } } } public class SubjectA : Subject<string> { public void SendMessage(string message) { Notify(message); } } public abstract class Observer<T> { public abstract void Update(T message); } public class ObserverA : Observer<string> { public override void Update(string message) { Console.WriteLine("Received the Message in ObserverA: " + message); } } public class ObserverB : Observer<string> { public override void Update(string message) { Console.WriteLine("Received the Message in ObserverB: " + message); } } public static void Main(string[] args) { SubjectA subject = new SubjectA(); subject.AttachObserver(new ObserverA()); subject.AttachObserver(new ObserverB()); subject.SendMessage("Hello, World!"); } } | cs |
1 2 3 | Received the Message in ObserverA: Hello, World! Received the Message in ObserverB: Hello, World! Press any key to continue . . . | cs |
위의 예제에서는 대상 객체와 관찰자 객체를 추상화하여 일반화된 상태를 통지할 수 있도록 하였다. 클라이언트는 ObserverA, ObserverB객체를 SubjectA객체에게 구독시킨 뒤 SubjectA객체의 SendMessage(T) 메소드를 통해 구독한 관찰자들에게 통지하였다. SubjectA 객체는 메소드의 인자를 통해 string의 메시지(상태)를 전달하였으므로 예제에서의 관찰자 패턴은 푸시 방식으로 구현되었다.
'PROGRAMMING > Design Pattern' 카테고리의 다른 글
[GoF] 상태 (State) 패턴 (0) | 2019.03.09 |
---|---|
[GoF] 명령 (Command) 패턴 (0) | 2019.03.09 |
[GoF] 책임 연쇄 (Chain of Responsibility) 패턴 (0) | 2019.03.09 |
[GoF] 메멘토 (Memento) 패턴 (0) | 2019.03.09 |
[GoF] 싱글턴 (Singleton) 패턴 (0) | 2019.03.09 |