< Dev-Kidult />

디자인 패턴 - 옵저버패턴(Observer pattern) with jdk 본문

개발/Back-end

디자인 패턴 - 옵저버패턴(Observer pattern) with jdk

개른이 2019. 7. 11. 00:36

옵저버패턴은 영문 그대로 관찰자가 존재하는 디자인 패턴입니다.

관찰자(observer)와 관찰당할 대상(observable)이 존재하며 관찰당하는(?) 대상이 무언가의 액션이나 상태변화가 일어나면 관찰자에게 알려주는 방식입니다.

 

옵저버패턴을 구현함에 있어서 많은 방법이 있지만

해당 글에서는 jdk에 구현되어 있는 observer interface와 observable class를 상속받아 구현하는 방식을 진행하도록 하겠습니다.

 

진행에 앞서 observable은 날씨 상태를 가지고 있는 객체를 observer는 날씨 상태변화에 따라 작동하는 객체들로 구성을 하였습니다.

class WeatherData extends Observable {
    private double temperature; // 온도
    private double humidity; // 습도
    private WeatherStatus weatherStatus; // 날씨상태

    WeatherData() {
    }

    void setMeasurements(double temperature, double humidity, WeatherStatus weatherStatus) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.weatherStatus = weatherStatus;
        measurementsChanged();
    }

    private void measurementsChanged() {
        setChanged();
        notifyObservers();
    }

    double getTemperature() {
        return temperature;
    }

    double getHumidity() {
        return humidity;
    }

    WeatherStatus getWeatherStatus() {
        return weatherStatus;
    }
}

 

WeatherData객체에는 각각 온도와 습도 그리고 날씨 상태를 가지고 있으며, 날씨 상태를 변경할 수 있는 setMeasurements 함수 그리고 각각 필드에 대한 getter를 구현하였습니다.

 

그리고 setChanged와 notifyObservers는 jdk observable에 구현되어 있는 함수이며, 해당 함수들을 통하여 관찰자들에게 정보를 전달하게 됩니다.

 

 

이어서 observer를 상속받아 WeatherData를 활용할 수 있는 객체들을 생성해 줍니다.

public class AirConditioner implements Observer {
    private Observable observable;
    private boolean active;
    private double temperature;

    AirConditioner(Observable observable) {
        this.active = false;
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            double _temperature = ((WeatherData) o).getTemperature();
            if (active) {
                if (_temperature < 24) {
                    stop();
                    this.active = false;
                    this.temperature = _temperature;
                } else if (_temperature < 28 && this.temperature > 34) {
                    normalMode();
                    this.temperature = _temperature;
                } else if (_temperature <= 34) {
                    turboMode();
                    this.temperature = _temperature;
                }
            } else {
                if (_temperature >= 34) {
                    turboMode();
                    this.active = true;
                    this.temperature = _temperature;
                } else if (_temperature > 28) {
                    normalMode();
                    this.active = true;
                    this.temperature = _temperature;
                }
            }
        }
    }

    private void stop() {
        System.out.println("쾌적환 환경입니다. 에어컨 작동을 중지합니다.");
    }

    private void normalMode() {
        System.out.println("후덥지근한 날씨가 되었습니다. 에어컨 작동을 시작합니다.");
    }

    private void turboMode() {
        System.out.println("더워 못살겠습니다. 터보모드를 시작합니디.");
    }
public class Dehumidifier implements Observer {
    private Observable observable;
    private boolean active;
    private double humidity;

    Dehumidifier(Observable observable) {
        this.active = false;
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            double _humidity = ((WeatherData) o).getHumidity();
            if (active) {
                if (_humidity < 40) {
                    stop();
                    this.active = false;
                    this.humidity = _humidity;
                } else if (_humidity < 60 && this.humidity > 80) {
                    normalMode();
                    this.humidity = _humidity;
                } else if (_humidity <= 80) {
                    turboMode();
                    this.humidity = _humidity;
                }
            } else {
                if (_humidity >= 80) {
                    turboMode();
                    this.active = true;
                    this.humidity = _humidity;
                } else if (_humidity > 60) {
                    normalMode();
                    this.active = true;
                    this.humidity = _humidity;
                }
            }
        }
    }

    private void stop() {
        System.out.println("쾌적환 환경입니다. 제습기 작동을 중지합니다.");
    }

    private void normalMode() {
        System.out.println("습도가 기준치 이상입니다. 제습모드를 시작합니다.");
    }

    private void turboMode() {
        System.out.println("실내 습도가 높습니다. 터보모드를 시작합니디.");
    }
}
public class WeatherApp implements Observer {
    private Observable observable;

    WeatherApp(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            notifyMessage((WeatherData) o);
        }
    }

    private void notifyMessage(WeatherData weatherData) {
        WeatherStatus weatherStatus = weatherData.getWeatherStatus();
        switch (weatherStatus) {
            case RAINY:
                System.out.println("비가 옵니다. 우산을 챙겨주세요.");
                break;
            case SNOWY:
                System.out.println("눈이 옵니다. do you wanna build a snowman?");
                break;
            case SUNNY:
                System.out.println("맑은 날입니다! 빨래를 널어도 좋을꺼 같아요.");
                break;
            case CLOUDY:
                System.out.println("흐린날이네요. 곧 비가 올지도 모르겠어요.");
                break;
            default:
                System.out.println("등록된 상태가 없어요.");
                break;
        }
    }
}

public enum WeatherStatus {
    SUNNY,
    RAINY,
    SNOWY,
    CLOUDY
}

 

온도 데이터를 받아 에어컨을, 습도 데이터를 받아 제습기를 그리고 날씨 상태를 받아 노티를 주는 어플을 간략히 구현해봤습니다.

 

이제 객체를 생성할 때 관찰할 대상(observable)을 넣어 생성하게 되면 관찰 할 대상(observable)에서 상태가 변경될 때마다 조건에 맞게 작동을 할 것입니다.

public class Main {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        Dehumidifier dehumidifier = new Dehumidifier(weatherData);
        WeatherApp weatherApp = new WeatherApp(weatherData);
        AirConditioner airConditioner = new AirConditioner(weatherData);

        weatherData.setMeasurements(30, 10, WeatherStatus.SUNNY);
        /*후덥지근한 날씨가 되었습니다. 에어컨 작동을 시작합니다.
        맑은 날입니다! 빨래를 널어도 좋을꺼 같아요.*/
        weatherData.setMeasurements(23, 90, WeatherStatus.RAINY);
        /*쾌적환 환경입니다. 에어컨 작동을 중지합니다.
        비가 옵니다. 우산을 챙겨주세요.
        실내 습도가 높습니다. 터보모드를 시작합니디.*/
        weatherData.setMeasurements(35, 55, WeatherStatus.CLOUDY);
        /*더워 못살겠습니다. 터보모드를 시작합니디.
        흐린날이네요. 곧 비가 올지도 모르겠어요.
        습도가 기준치 이상입니다. 제습모드를 시작합니다.*/
    }
}

 

이와 같이 weatherData의 데이터만 변경되었음에도 관찰자들인 제습기 앱 에어컨이 작동함을 볼 수 있습니다.

 

코드를 자세히 보셨다면 아시겠지만 observable이 클래스인 점, 그로 인해 다중 상속이나 몇몇 함수들이 protected로 구현되어있어 재사용에 불편이 있는 점등이 있습니다.

 

다음 포스팅은 observer 및 observable을 직접 구현하는 포스팅으로 찾아뵙겠습니다.

반응형
Comments