옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고신문 구독으로 간단한 예를들 수 있다.
자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의합니다.
신문사는 새로운 신문을 발행한다. 신문을 구독 중인 고객들에게는 새 신문이 배달되고, 고객들은 자동으로 구독해지, 신청을 언제든지 할 수 있다.
신문사는 구독 중인 고객들에게 신문을 배달하는 순서는 신경쓰지 않는다.
간단한 기상 스테이션을 Observer 패턴으로 구현해보자.
다음과 같이 subject, observer 인터페이스를 만들어 간단히 구현 가능하다.
public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } public interface Observer { public void update(float temp, float humidity, float pressure); } public interface DisplayElement { public void display(); }신문사에 해당하는 Subject 인터페이스와 고객인 Observer 인터페이스, 기상 정보를 표현할 DisplayElement 인터페이스로 구성되어 있다.
public class WeatherData implements Subject { private final ArrayList<Observer> observers; private float pressure; private float humidity; private float temperature; public WeatherData() { this.observers = new ArrayList<Observer>(); } public void setMeasurements(int temperature, int humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } private void measurementsChanged() { notifyObservers(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for(Observer o : observers) { o.update(this.temperature, this.humidity, this.pressure); } } } public class CurrentConditionsDisplay implements Observer, DisplayElement { private final Subject weatherData; private float humidity; private float temperature; public CurrentConditionsDisplay(WeatherData weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void display() { System.out.println("Current conditions: " + this.temperature + "F degrees and " + this.humidity + "% humidity"); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } }
WeatherData 클래스는 Observer들을 관리할 Observer 리스트를 가지고 있으며, registerObserver를 통해 등록된 Observer들에게 기상정보가 변경될 때 update메세지를 보낸다.
Observer인 CurrentConditionsDisplay 클래스는 weatherData의 registerObsever를 통해 Subject 클래스로 자신의 참조를 전달하고, Subject에서 update를 호줄하면 예정된 동작을 실행하게 된다.
현재 클래스에서는 생성자에서 register를 했으나 원할 때 remove, register 가능하다.
자바에서 Observer 패턴을 위한 기능이 있다.
java.util.Observable; java.util.Observer;위의 WeatherData와 CurrentConditionsDisplay를 아래와 같이 변경가능하다.
public class WeatherData extends Observable { private float pressure; private float humidity; private float temperature; public void setMeasurements(int temperature, int humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } private void measurementsChanged() { setChanged(); notifyObservers(); } } public class CurrentConditionsDisplay implements Observer, DisplayElement { private final Observable weatherData; private float humidity; private float temperature; public CurrentConditionsDisplay(Observable weatherData) { this.weatherData = weatherData; weatherData.addObserver(this); } @Override public void display() { System.out.println("Current conditions: " + this.temperature + "F degrees and " + this.humidity + "% humidity"); } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherData) { WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } }
주의할 점은 Observable이 인터페이스가 아니라 Class라는 점이다. 따라서 Observer를 위한 자료구조를 만들 필요가 없으며, addObserver/deleteObserver 등의 메소드가 이미 구현되어 있다.
implements가 아니라 extends해야 하기 때문에 사용에 제약이 있을 수 있다.
setChanged()를 호출한 후 notifyObservers()를 호출하면 등록된 Observer들의 update가 호출된다.
Observer들은 update(Observable, Object)를 통해 메세지를 받는다.
Observable은 update를 호출한 객체를 전달하고, Object에는 Observable에서 notifyObservers(Object)로 넘겨준 param이 전달된다.(notifyObservers()는 notifyObservers(null)과 같다.)
위 코드에서 처럼 Observable의 getter를 통해 데이터를 가져와도 되고(pull), notifyObservers(Object)로 넘겨줘도 된다.(push)
참고도서 :
- Head First Design Patterns - 스토리가 있는 패턴학습법 | 케이시 시에라 | 버트 베이츠 | 엘리자베스 프리먼 | 에릭 프리먼 (지은이) | 서환수 (옮긴이) | 한빛미디어(한빛아카데미)
댓글 없음:
댓글 쓰기