-
[Design Pattern] 옵저버(Observer) 패턴 - 디자인 패턴CSE/Design Pattern 2015. 6. 13. 10:47
Pattern #16 옵저버 패턴
한 객체에 의하여 영향을 받는 객체들을 정리하는데 사용
패턴 요약
- 단일 객체가 영향받는 객체 집합에 대하여 같은 이름의 메소드를 호출하여 구현
- 관찰 대상 객체가 관찰하는 객체가 몇 개인지 어떤 클래스인지 모름
동기
당신은 기업정보 입출력 프로그램을 개발 중이다. 이 프로그램을 이용하여 사용자들은 DB에 저장되어 있는 기업정보(월 판매액, 매출
증가량, …)를 여러 가지 형태(도표, 꺾은선그래프, 막대그래프,..)로 볼 수 있어야 한다. 또한 관리자들이 특정 기업정보를 갱신한다면
해당 정보를 보여주고 있는 모든 View들은 변경된 정보를 다시 반영하여 보여 주어야 한다 . 기업정보(Model) 과 보여주는
화면(View)과의 관계를 어떻게 설계할 것인가?
해결방안
- Model(data)이 변경되면 이를 각 view(화면)들에게 알려줌
- Model은 자신의 정보변경에 대한 notification을 받기를 원하는 뷰들에 대한 레퍼런스를 가지고 있어야 한다.
해결방안2
- 정보변경 통보를 받은 뷰들은 모델에게서 변경된 정보를 얻어옴
- 각 뷰들은 자신이 필요로 하는 모델에 대한 레퍼런스를 가지고 있으며, 모델에게 원하는 정보를 얻어 올 수 있다.
해결방안3
해결방안4
- Model class도 향후 확장을 고려하여 class hierarchy로 구성
- Observer pattern에서는 통지하는 역할을 Subject, 통지 받는 역할을 Observer라고 얘기함.
의도
- 한 객체의 상태가 변하면 관련된 다른 모든 객체들이 해당 상태변화를 알수 있도록 객체들 간의 one-to-many 의존관계를 정의
별칭
- Dependents, Publish-Subscribe
적용범위
- 시스템의 한 부분이 다른 부분에 의존적일 때, 양자를 개별적인 객체로 만들고, Observer pattern을 적용하면 독립성과 재사용성을 높일 수 있다.
- 한 객체의 상태가 변경되면 다른 객체들도 따라 변경될 필요가 있을때
- 얼마나 많은 객체들이 따라서 변경되어야 하는지 알 필요가 없을 때
- 변경을 알리는 객체와 변경을 통지 받는 객체간의 연결강도를 줄일 때
결과
- Subject객체와 Observer객체간의 coupling을 줄임
* Subject객체는 단지 Observer 객체 list를 가지고 있다는 정도만 알면 된다.
* Subject class와 Observer class가 서로 독립적으로 변경 및 확장될 수 있다.
- Broadcast 통신에도 이용 가능
* Observer 객체들이 원하는 subject 객체에 등록하지 않고, subject의 상태가 변화되면 전체 객체들에게 상태변화를 broadcast함
- 예상치 못한 updates 발생가능
* 관찰자 객체들(Observers) 간에는 상호존재를 인식하지 못하기 때문에, 한 Subject 객체의 상태변화가 전체적으로 어느 정도 만큼의 Observer 변화 비용이 드는지 알지 못함
예제 1.
Subject.java
1234567891011package observer1;public interface Subject {public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyAllObservers();}cs Observer.java
1234567package observer1;public interface Observer {public void update(Subject s);}cs HeadHunter.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344package observer1;import java.util.ArrayList;public class HeadHunter implements Subject {private ArrayList<Observer> userList;private ArrayList<String> jobs;public HeadHunter() {userList = new ArrayList<Observer>();jobs = new ArrayList<String>();}public void registerObserver(Observer o) {userList.add(o);}public void removeObserver(Observer o) {}public void notifyAllObservers() {for (Observer o : userList) {o.update(this);}}public void addJob(String job) {this.jobs.add(job);notifyAllObservers();}public ArrayList<String> getJobs() {return jobs;}public String toString() {return jobs.toString();}}cs JobSeeker.java
12345678910111213141516171819package observer1;public class JobSeeker implements Observer {private String name;public JobSeeker(String name) {this.name = name;}public void update(Subject s) {System.out.println(this.name + " got notified");System.out.println(s);}}cs ObserverMain.java
123456789101112131415161718package observer1;public class ObserverMain {public static void main(String[] args) {HeadHunter hh = new HeadHunter();hh.registerObserver(new JobSeeker("Kim"));hh.registerObserver(new JobSeeker("Lee"));hh.registerObserver(new JobSeeker("Park"));hh.addJob("Google Job");hh.addJob("Apple Job");}}cs 예제2.Subject.java123456789package observer2;public interface Observer {public void update();public void setSubject(Subject sub);}cs Observer.java12345678910111213package observer2;public interface Subject {public void register(Observer obj);public void unregister(Observer obj);public void notifyObservers();public Object getUpdate(Observer obj);}cs MyTopic.java1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859package observer2;import java.util.ArrayList;import java.util.List;public class MyTopic implements Subject {private List<Observer> observers;private String msg;private boolean isChanged;private final Object MUTEX = new Object();public MyTopic() {this.observers = new ArrayList<>();}public void register(Observer obj) {if (obj == null)throw new NullPointerException("Null Observer");synchronized (MUTEX) {if (!observers.contains(obj))observers.add(obj);}}public void unregister(Observer obj) {synchronized (MUTEX) {observers.remove(obj);}}public void notifyObservers() {List<Observer> observersLocal = null;synchronized (MUTEX) {if (!isChanged)return;observersLocal = new ArrayList<>(this.observers);this.isChanged = false;}for (Observer obj : observersLocal) {obj.update();}}public Object getUpdate(Observer obj) {return this.msg;}public void postMessage(String msg) {System.out.println("Message Posted to Topic : " + msg);this.msg = msg;this.isChanged = true;notifyObservers();}}cs MyTopicSubscriber.java12345678910111213141516171819202122232425262728package observer2;public class MyTopicSubscriber implements Observer {private String name;private Subject topic;public MyTopicSubscriber(String name) {this.name = name;}public void update() {String msg = (String) topic.getUpdate(this);if (msg == null) {System.out.println(name + ":: No New Message");} else {System.out.println(name + ":: Consuming message::" + msg);}}public void setSubject(Subject sub) {this.topic = sub;}}cs ObserverMain.java123456789101112131415161718192021222324252627package observer2;public class ObserverMain {public static void main(String[] args) {MyTopic topic = new MyTopic();Observer obj1 = new MyTopicSubscriber("Obj1");Observer obj2 = new MyTopicSubscriber("Obj2");Observer obj3 = new MyTopicSubscriber("Obj3");topic.register(obj1);topic.register(obj2);topic.register(obj3);obj1.setSubject(topic);obj2.setSubject(topic);obj3.setSubject(topic);obj1.update();topic.postMessage("New Message");}}cs 'CSE > Design Pattern' 카테고리의 다른 글
[Design Pattern] 메멘토(Memento) 패턴 - 디자인 패턴 (0) 2015.06.13 [Design Pattern] 커멘드(Command) 패턴 - 디자인 패턴 (0) 2015.06.13 [Design Pattern] 상태(State) 패턴 - 디자인 패턴 (0) 2015.06.13 [Design Pattern] 중재자(Mediator) 패턴 - 디자인 패턴 (0) 2015.06.13 [Design Pattern] 반복자(Iterator) 패턴 - 디자인 패턴 (0) 2015.06.13 [Design Pattern] 인터프리터(Interpreter) 패턴 - 디자인 패턴 (0) 2015.06.13