Observer Pattern
옵져버 패턴은 객체 사이에 일 : 다 의존 관계 를 정의해두어 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 만드는 패턴이다. 이런 종류의 상호작용을 게시 - 구독 관계라 한다.
활용성
한 객체에 가해진 변경으로 다른 객체를 변경해야 하고 프로그래머들은 얼마나 많은 객체들이 변경되어야 하는지 몰라도 될 때
어떤 객체가 다른 객체에 자신의 변화를 통보할 수 있는데 그 변화에 관심있어 하는 객체들이 누구인지에 대한 가정 없이도 그러한 통보가 될 때
옵저버 패턴 사용에 따른 결과 Subject - Observer간 추상적 결합 Subject 클래스와 Observer 클래스의 약한 결합덕분에 시스템에서 이들의 서브 클래스를 수용할 수 있다.
브로드 캐스트 방식의 교류 구체적인 수신자를 송신자에서 알 필요가 없기 때문에 브로드 캐스트 방식의 통신을 구현할 수 있다.
예측하지 못한 정보 갱신 종속성을 기준으로 상태를 갱신하기 때문에 불필요한 갱신이 일어날 수 있다. 또한 추적도 까다로울 수 있다. 어떤 정보가 갱신되었는지는 옵저버가 무엇이 변경되었는지 알 수 있게 해 주는 별도의 프로토콜이 추가되지 않는 한 변경을 유추하는 작업은 어렵다.
옵저버 패턴 vs 중재자 패턴 옵저버 패턴을 사용해서 중재자 패턴을 구현할 수 있다. 중재자패턴에서는 Colleague가 중재자를 참조할 수 있다. 중재자가 Colleague를 참조하는 것은 옵저버 패턴의 형태와 동일하다. 즉 어떤 참조를 구독하고 있는 경우에서 알림을 보낼때는 옵저버 패턴을 사용하고 다수의 클라이언트간에서 상호간의 통신을 해야할 때 즉, 1 : N의 관계보다 1 : 1의 단순한 관계를 유지해야할 때는 중재자 패턴을 사용한다.
옵저버 구조 옵저버의 구조는 다음과 같다.
C++ 구현 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void ObserverPattern::ObserverPattern () { Subject* concreteSubject = new ConcreteSubject (); Observer* observer1 = new ConcreteObserver ("observer1" ); Observer* observer2 = new ConcreteObserver ("observer2" ); Observer* observer3 = new ConcreteObserver ("observer3" ); Observer* observer4 = new ConcreteObserver ("observer4" ); concreteSubject->attach (observer1); concreteSubject->attach (observer2); concreteSubject->attach (observer3); concreteSubject->notify ("Notification 1" ); concreteSubject->detach (observer1); concreteSubject->attach (observer4); concreteSubject->notify ("Notification 2" ); }
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 class Subject { public : virtual ~Subject () {} virtual void attach (Observer* observer) = 0 ; virtual void detach (Observer* observer) = 0 ; virtual void notify (std::string notification) = 0 ; }; class ConcreteSubject : public Subject{ std::set<Observer*> observers; public : void attach (Observer* observer) override { observers.insert (observer); } void detach (Observer* observer) override { observers.erase (observers.find (observer)); } void notify (std::string notification) override { for (Observer* observer : observers) observer->update (notification); } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Observer { public : virtual ~Observer () {} virtual void update (std::string notification) = 0 ; }; class ConcreteObserver : public Observer{ std::string name; public : ConcreteObserver (std::string name) : name (name) {} ~ConcreteObserver () {} void update (std::string notification) override { std::cout<<name<<" received : " <<notification<<std::endl; } };
java 구현 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Client { public static void main (String[] args) { Subject subject = new ConcreteSubject (); Observer observer1 = new ConcreteObserver ("observer 1" ); Observer observer2 = new ConcreteObserver ("observer 2" ); Observer observer3 = new ConcreteObserver ("observer 3" ); Observer observer4 = new ConcreteObserver ("observer 4" ); subject.attach(observer1); subject.attach(observer2); subject.attach(observer3); subject.notify("Notification 1" ); subject.detach(observer1); subject.attach(observer4); subject.notify("Notification 2" ); } }
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 public interface Subject { void attach (Observer observer) ; void detach (Observer observer) ; void notify (String notification) ; } public class ConcreteSubject implements Subject { private List<Observer> observers; public ConcreteSubject () { observers = new ArrayList <>(); } @Override public void attach (Observer observer) { observers.add(observer); } @Override public void detach (Observer observer) { observers.remove(observer); } @Override public void notify (String notification) { for (Observer observer : observers) observer.update(notification); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public interface Observer { void update (String state) ; } public class ConcreteObserver implements Observer { private String name; public ConcreteObserver (String name) { this .name = name; } @Override public void update (String state) { System.out.println(name + " received : " + state); } @Override public boolean equals (Object obj) { if (!(obj instanceof ConcreteObserver)) return false ; ConcreteObserver concreteObserver = (ConcreteObserver)obj; return this .name.equals(concreteObserver.name); } }