-
2. 옵저버 패턴(Observer Pattern)카테고리 없음 2020. 2. 25. 13:05
1. Class Diagram
2. Observer Pattern?
* 옵저버 패턴의 구성요소
출판사 + 구독자 = 옵저버패턴 출판사 = 주제(subject) 구독자 = 옵저버(observer)
옵저버 패턴의 구성요소만 외우자.
이걸 좀 더 자세하게 풀어서 이야기하면, 구독자들이 출판사에 구독을 신청하면, 출판사는 새로운 책이 출간될 때마다 구독자들에게 책을 나눠준다고 생각하자. 그리고 구독자들은 구독을 원치않으면 구독을 취소할 수도 있다는 것과 동일한 개념.
즉, 하나의 주제(Subject)객체에 여러 옵저버(Observer) 객체들이 구독을 신청(add)하면, 주제 객체는 변경사항이 생길때마다(update) 옵저버들에게 변경데이터를 전달한다. 그리고 옵저버들은 해당 데이터가 더 이상 필요하지 않다면 구독 취소(remove)할 수도 있다.
좀 더 전문적으로 말하면, 옵저버패턴은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.
3. 느슨한 결합(Loose Coupling)
두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다(서로가 긴밀하게 연결되어있지 않음을 의미). 따라서, 옵저버 패턴은 이 느슨한 결합을 사용하여 언제든 subscriber들이 구독하고 구독 취소를 할 수 있도록 디자인 된 것이라고 보면된다.
4. Code (온도를 측정해서 여러 Display 장비에 뿌려주기)
0) 코드구조
1) Subject Object (Weather Data 객체)
package observer; import java.util.ArrayList; public class WeatherData implements Subject { // 여기에서 볼 수 있듯이 얘는 Subject private ArrayList observers; private float temprature; private float humidity; private float pressure; public WeatherData() { // 생성자 observers = new ArrayList(); } @Override public void registerObserver(Observer o) { // 옵저버 등록 observers.add(o); } @Override public void removeObserver(Observer o) { // 삭제 int i = observers.indexOf(o); // 해당 옵저버 객체를 찾고 if (i>=0) { observers.remove(i); // 삭제 } } @Override public void notifyObservers() { // 변경이 일어나면 모든 객체에 변경 사항을 업데이트 for (int i = 0; i<observers.size(); i++) { Observer observer = (Observer) observers.get(i); // 객체를 가져와서 업데이트 observer.update(temprature,humidity,pressure); } } public void measurementsChanged() { notifyObservers(); } public void setMeasurements(float temprature, float humidity, float pressure){ // 값을 set 하라는 명령이 들어오면 this.temprature = temprature; //아래의 모든 변수의 값을 업데이트하고 this.humidity = humidity; this.pressure = pressure; measurementsChanged(); // 모든 옵저버에게 알림 } }
2) Observer Object (CurrentConditionDisplay)
package observer; public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); // 현재 객체를 Subject에 등록 } @Override public void display() { System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity"); } @Override public void update(float temp, float humidity, float pressure) { // update가 호출되면 기온/습도를 저장 후 display() 호출 this.temperature = temp; this.humidity = humidity; display(); } }
3) 설명
위의 두 객체의 구조는
Weather Data = Subject(출판사)
CurrentConditionDisplay = Subscriber(구독자)
의 구조이다.
즉, Weather Data는 새로운 데이터가 생기면 언제든 CurrentConditionDisplay에 데이터를 전달한다.
또한, CurrentConditionDisplay는 언제든 Weather Data에 새롭게 구독을 신청하거나 구독을 취소할 수 있다.
5. 결론
Observer pattern은 실제로 Spring에서 @ApplicationEventPublisher이 이벤트 프로그래밍에 필요한 인터페이스를 제공하기 위해 옵저버 패턴 구현체로 만들어두었다. 적어도 스프링에서 쓰이는 개념이니 스프링을 시작하기 전에 미리 알아두면 좋을 것 같다.
사실 이 디자인 패턴이 사용되는건 주로 위의 코드에서 보여준 것처럼, 주기적으로 데이터를 보내는 주체와 그것을 받는 대상이 존재하는 경우에 사용되는거 같은데, 특히, 해당 대상에 추가/삭제가 자주 일어나는 경우에 사용한다면 더 좋지 않을까 싶다.(개인적인 생각)
어쨌든, 아래의 내용만 꼭 기억하자.
출판사+구독자 = 옵저버패턴
출판사 = 주제(Subject)
구독자 = 옵저버(Observer)
6. 참고자료 (자바 내장 옵저버 객체)
1) 사용방법
자바에 내장된 옵저버 패턴은 위에서 구현한 것과는 다르게 작동한다. 가장 큰 차이점은 주제객체에서 Observable 클래스를 확장하면서 addObserver, deleteObserver, notifyObservers 같은 메소드를 상속받는다. java.util.Observer를 호출해서 사용할 수 있다.
2) 단점
a. Observable이 클래스이기 때문에 서브클래스를 만들어야하며, 그래서 재사용성에 제약이 생기게 된다.
b. Observable 인터페이스라는 것이 없기 때문에 자바에 내장된 Observer API하고 잘 맞는 클래스를 직접 구현하는 것이 불가능하다.
c. Observable 클래스의 핵심 메소드를 외부에서 호출할 수 없다.
3) 결론
가능하면 java 내장 객체를 쓰기보다는 객체를 만들어서 쓰는게 더 좋다.