본문 바로가기

성장하는 중 입니다?/디자인패턴

자바 내장 옵저버 패턴

이 포스트는 한빛미디어 'HeadFirst - Design Pattern' 을 공부하면서 작성되었습니다.

 

 

 

이전 포스트에서 구현했던 Subject 인터페이스 대신, Observable 클래스를 통해 옵저버 패턴을 구현할 수 있다. 

java.util.Observer, java.util.Observable 을 이용하면 된다. 

 

Class Observable Class WeatherData
: Observable 를 확장한 Class (Observable Object)
addObserver()
deleteObserver()
notifyObserver()
setChanged()
getTemperature()
getHumidity()
getPressure()

 

Interface Observer GeneralDisplay
StatisticsDisplay
ForecastDisplay
: Observer 를 구현하는 Concrete Class
update() update()
display()

 

 

Class Observable, Interface Observer 구조를 가진 자바 내장 옵저버 패턴은 이전 글에서 직접 구현한 옵저버 패턴과 약간 다르다. 

WeatherData 가 Observable Class 를 확장하고 add/delete/notify 와 같은 methods 를 상속한다는 점이 가장 다르다.

 

Object 이 observer 가 되기 위해 ..

java.util.Observer interface 를 구현한 후, Observable object 에서 addObserver()를 부른다. 마찬가지로 어떤 observer 를 삭제하기 위해 deleteObserver()를 부른다. 

 

Observable 이 알람을 보내기 위해 ..

java.util.Observable superclass 를 확장하여 Observable 로 만든 후, 아래 두 단계를 수행한다.

1. setChanged() method 실행, 객체에서 상태가 바뀌었다는 것을 표시하기 위해,

2. 두 notifyObservers() methods 중 하나 실행 (notifyObservers() or notifyObservers(Object arg)

 

Observer 가 알람을 받기 위해 ..

Observer 는 update method를 구현하는데, 약간 다르다. observer들에게 데이터를 보내고 싶다면(push), notifyObservers(arg) method로 데이터를 넘기면 된다. 메세지 전송을 관두고 싶다면(pull), Observable object 가 데이터를 넘겼던 곳으로부터 Observer는 데이터를 빼낸다. 

 

setChanged() -> notifyObservers() : 날씨 상태가 바뀌었으며, 그 정보를 observer들에게 업데이트 함

setChanged() 가 불리지 않고 notifyObservers() : observer들은 알람을 받지 않음

 

 

ㅇㅗㅏ 먼말이야 구현을 먼저 보자

 

// Observable : superClass
import java.util.Observable;


// superClass의 subClass 생성 : WeatherData
public class WeatherData extends Observable {

    // 우리는 더이상 observer 등록/삭제/알람 관리를 직접 할 필요가 없다.
    private float temperature;
    private float humidity;
    private float pressure;
    
    // 더이상 Observers에게 연락하기 위해 data structure를 생성할 필요가 없다.
    public WeatherData() {}
    
    public void measurementsChanged() {
    
        // 이제 notifyObservers 호출을 통해 data 객체를 보내지 않고, PULL 모델을 쓴다. 
    	setChanged();
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float 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;
    }
}

 

 

Current Display 모듈

 

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

public class CurrentConditionDisplay implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;
    
    public CurrentConditionDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);    
    }
    
    public void update(Observable obs, Object arg) {
        if(obs instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)obs;
            
            // Tmep, Humidity 정보를 업데이트
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            
            display();
        }
    }
    
    public void display() {
        System.out.println("Current condition: " + temperatur + "F degrees and " 
        + humidity + "% humidity");
    }
}

 

 

Forecast Display 모듈

 

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

public class ForecastDisplay implements Observer, DisplayElement {
    Observable observable;
    private float currentPressure = 29.92f;
    private float lastPressure;

    public ForecastDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
    
    public void update(Observable observable, Object arg) {
        if(observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
        
            // 마지막 기압과 현재 기압 저장
            lastPressure = currentPressure;
            currentPressure = weatherData.getPressure();
        
            display();
        }
    }
    
    public void display() {
        System.out.print("Forecast: ");
		if (currentPressure > lastPressure) {
			System.out.println("Improving weather on the way!");
		} else if (currentPressure == lastPressure) {
			System.out.println("More of the same");
		} else if (currentPressure < lastPressure) {
			System.out.println("Watch out for cooler, rainy weather");
		}
    }
}

 

 

테스트 결과 : 

 

직접 구현한 옵저버패턴의 결과와 순서가 다름

Forecast -> Statistics -> Current

 

 

registerObserver 에서 데이터를 저장하는 순서가 다르기 때문?!

 

사실 자바 내장 옵저버패턴은 몇 가지 불편한 점이 있다.

1. Observable 은 class 다. interface가 아니고

Observable 이 class이기 때문에 이것을 subclass 해야한다. 그러면 Observable behavior 에 추가하는 작업을 할 수 없다. WeatherData subclass는 이미 Observable superclass 를 extend 하기 때문에, 이 subclass 에 behavior를 추가할 수 없는 것.

2. Obsevable 이 crucial methods 를 방지한다.

setChanged() 함수는 Observable API 이기 때문에, Observable 을 subclass 하지 않으면 이 함수를 호출할 수 없다.

'성장하는 중 입니다? > 디자인패턴' 카테고리의 다른 글

옵저버 패턴  (0) 2021.07.07