1. 상황
기상정보를 수집하여 디스플레이 장비에 보여주게 되는 시스템을 만드려고 한다.
기상정보는 습도, 온도, 압력의 데이터가 있고 이 데이터를 WeatherData 라는 객체가 수집하여
디스플레이 할 수 있도록 해야하는 것이다.
단, 디스플레이되는 부분은 변경될 수 있고, 디스플레이 서비스를 받고자 하는 장비도 추가 또는 삭제될 수 있다.
2. 해결방안
WeatherData 객체에서 알수 있는것은 온도,습도,압력을 가져오는 메소드와 기상관측값이 변경될때 마다 알려주는
measurementChanged() 이다.

그리고 WeatherData 객체를 이용해 세가지 형태의 디스플레이로 표현되야한다고 하면,
새로운 측정값이 올때마다 디스플레이도 갱신되어야 한다.
만약 다른 개발자들이 다른 디스플레이 항목을 추가하도록 하고 디스플레이 방식이 추가/삭제
된다는 것도 염두해 두어야 하는것이다.
이를 코드로 표현해본다면..
public class WeatherData {
public float getTemperature() {
return 온도
}
public float getHumidity() {
return 습도
}
public float getPressure() {
return 압력
}
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
}
위와 같은 형식으로 짜볼 수 있다.
그러나 지금까지 우리가 중요하게 생각하는 변경되는 부분에 대해 캡슐화를 해야한다고 알고 있다.
세가지 형태의 디스플레이되는 부분은 추가/삭제될 수 있다고 했으니 이부분을 캡슐화해야한다.
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
이 문제를 해결할 수 있는 최적의 패턴이 Observer Pattern (옵저버 패턴) 이다.
옵저버 패턴을 이해하기 위해 신문 구독하는 메카니즘에 대해 알아보자.
매일 갱신되는 신문에 대해 구독자에게 전달해 주고,
구독자는 해지하거나 가입할 수도 있다라는 간단한 메카니즘이다.
신문사를 주제(subject), 구독자를 옵저버(observer) 라고 하자.
주제가 변경되면 옵저버에게 주제가 전달되고 옵저버는 추가되거나 제거될 수도 있다.
이때 옵저버는 주제에게 등록요청을 하고 해지요청을 한다.
하나의 신문사에게 여러명의 구독자에게 정보를 전달하는 것은 일대다의 관계이다.
이는 옵저버의 상태를 관장하는 객체는 하나이고 (subject) 옵저버는 여러개란 얘기이다.
옵저버 패턴의 일반적인 그림이다.

그러면 위 옵저버 패턴을 이용해 기상스테이션의 그림을 그려보면.

위 그림은 CurrentConditionsDisplay만 표시했으나 실제로는 여러개의
Observer 인터페이스를 구현하는 클래스는 여러개이다.
Subject 인터페이스 내용이다.
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notityObservers();
}
Subject를 구현한 WeatherData 클래스의 내용이다.
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float pressure;
private float humidity;
public WeatherData(){
observers = new ArrayList();
}
public void notityObservers() {
for(int i =0; i < observers.size(); i++){
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(i);
}
}
public void measurementsChanged() {
notityObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
디스플레이되는 세가지 형태 중 한가지 예로 CurrentConditionsDisplay 객체 내용이다.
public class CurrentCoditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentCoditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
this.humidity = humidity;
this.temperature = temp;
display();
}
public void display() {
System.out.println("Current Conditions:"+ temperature + "F degrees and "+ humidity + "% hunmidity");
}
}
온도와 습도만 프린트 하는 형태이다.
이를 테스트 하는 코드이다.
public class WeatherDataClient {
/**
* @param args
*/
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentCoditionsDisplay currentCoditionsDisplay = new CurrentCoditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
}
}
테스트 출력결과이다.
Current Conditions:80.0F degrees and 65.0% hunmidity
Observer Pattern (옵저버 패턴) : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고
자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의합니다.