본문 바로가기

IT Book Summary/디자인패턴

Observer Pattern 옵저버 패턴

  • 중요한 일이 일어났을때 객체들에게 소식을 알려줄 수 있는 패턴
  • 객체 쪽에서 계속해서 정보를 받을지 여부를 실행중에 결정가능.
  • 일대다 관계와 느슨한 결합
  • JDK에서 가장 많이 쓰이는 패턴 가운데 하나

Weather-O-Rama 사의 기상 모니터링 애플리케이션 예제

- WeatherData 객체를 사용해 현재조건, 기상통계, 기상예측 세 항목을 디스플레이에 갱신해 보여주는 애플리케이션

 

기상정보를 수집하는 기상 스테이션

WeatherData 객체,

기상조건을 보여주는 디스플레이 로 이루어진다.

 

확인할 수 있는 규격

  • WeatherData 클래스에는 온도, 습도, 기압 세가지 측정값을 알아내는 Getter 메소드가 있다.
  • 새 기상데이터가 나올때마다 measurementsChanged() 메소드 호출
  • 현재조건, 기상통계, 기상예보 세개의 디스플레이 항목을 구현해야 함.
  • WeatherData 에 새로운 측정값이 들어올때마다 디스플레이를 갱신.
  • 시스템 확장 가능해야 함.
public class WeatherData {
  ...

  public void measurementChanged() {
    float temp = getTemperature();
    float humidity = getHumidity();
    float pressure = getPressure();
    
    // 디스플레이 갱신
    currentConditionsDisplay.update(temp, humidity, pressure);
    statisticsDisplay.update(temp, humidity, pressure);
    forecastDisplay.update(temp, humidity, pressure);
  }
  
  ...
}

// 바꿀수 있는 부분을 캡슐화 해야함.

 

 

옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고

자동적으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.

 

주제 객체와 다수의 딸린 객체를 가진 옵저버 객체로 구성.

주제(Subject) 인터페이스와 옵저버 (Observer) 인터페이스가 들어있는 클래스 디자인을 바탕으로 한다.

옵저버는 여러개가 있을수 있으며 주제에 의존적인 성질을 가진다.

 

Observer Pattern Class Diagram

 

Loose Coupling

옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어있는 객체디자인을 제공한다.

  • 주제는 옵저버가 특정 인터페이스를 구현한다는 것만 알고있다.
  • 옵저버는 언제든 추가가능
  • 새로운 형식의 옵저버를 추가해도 주제를 변경할 필요가 없다
  • 주제와 옵저버는 서로 독립적으로 재사용 가능
  • 주제와 옵저버는 바뀌더라도 서로 영향을 미치지 않음.

Weather Station Diagram

public interface Subject {
  // 옵저버 등록, 제거, 호출
  public void registerObserver(Observer o);
  public void removeObserver(Observer o);
  public void notifyObserver(Observer o);  
}

public interface Observer {
  // 기상정보가 변경되었을때 옵저버에게 전달되는 값
  public void update(float temp, float humidify, float pressure);
}

public interface DisplayElement {
  // 화면 표시 
  public void display();
}

 

 

WeatherData 에서 Subject 인터페이스 구현

 

public class WeatherData implements Subject { // 인터페이스 구현
  private ArrayList observers; // 옵저버 객체 저장
  private float temperature;
  private float humidity;
  private float pressure;
  
  public WeatherData() {
    observer = new ArrayList();
  }
  
  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 notifyObservers() {
    for(int i=0; i<observers.size(); i++) {
      Observer observer = (Observer)observers.get(i);
      observer.update(temperature, humidity, pressure); // 변한 상태 옵저버에 알려줌
    }
  }
  
  pubilc void measurementsChanged() {
    notifyObservers(); // 갱신 측정치를 알림
  }
  
  public void setMeasurements(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measuremetnsChanged();
  }
  
  ...
}

 

 

디스플레이 항목 구현

 

// 변경사항을 받기위한 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);
  }
  
  public void update(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    display();
  }
  
  public void display() {
    System.out.println("Current conditions: "+ temperature + "F degrees and "
    + humidity+ "% humidity");
  }
}

 

기상 스테이션 구동

 

public class WeatherStation {
  public static void main(String[] args) {
    WeatherData weatherData = new WeatherData();
    
    // 세개의 디스플레이 생성하며 WeatherData 객체를 인자로 전달.
    CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
    StatisticsDisplaly statisticsDisplay = new StatisticsDisplaly(weatherData);
    ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
    
    weatherData.setMeasurements(80, 65, 30.4f);
    weatherData.setMeasurements(82, 68, 29.4f);  
    weatherData.setMeasurements(78, 70, 29.6f);  
  }
}

 

 


자바 내장 옵저버 패턴 사용

 

자바 util 패키지에 들어있는 Observable 인터페이스와 클래스

푸시방식과 풀방식으로 갱신가능.

Observable class 사용

 

setChanged() 메소드는 상태가 바뀌었다는 것을 알기위한 용도.

 

import java.util.Observable;
import java.util.Observer;

public class WeatherData extends Observable { // 서브클래스
  private float temperature;
  private float humidity;
  private float pressure;
  
  public WeatherData() {
  }
    
  pubilc void measurementsChanged() {
    setChanged(); // 상태바뀜
    notifyObservers(); // 갱신 측정치를 알림
  }
  
  public void setMeasurements(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measuremetnsChanged();
  }
  
  // getter 메소드
  ...
}

 

 

import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {
  Observable observable;
  private float temperature;
  private float humidity;
  
  public CurrentConditionsDisplay (Observable observable) {
    this.observable = observable;
    observable.addObserver(this);
  ]

  public void update(Observable obs, Object arg) {
    if(obs instanceof WeatherData) {
      WeatherData weatherData = (WeatherData)obs;
      this.temperature = weatherData.getTemperature();
      this.humidity = weatherData.getHumidity();
      display();
    }
  }
  
  public void display() {
    System.out.println("Cuttent condiditions: "+temperature+ "F degrees and "
    +humidity+ "% humidity");
  }

}