Computer Theory/Design partterns

옵저버 패턴

JHeaon 2023. 9. 4. 12:40

 

옵저버 패턴 (Observer pattern)

옵저버 패턴이란 객체의 상태 변화를 관찰하는 관찰자들 즉 옵서버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 알려주는 패턴을 의미한다. 쉽게 말하자면 어떤 객체의 상태가 변할 때 그와 연관된 객체들에게 알림을 보내는 디자인 패턴을 의미한다. 

 

 

 

상황

Weather 객체로 현재 조건, 기상 통계, 기상 예보, 이렇게 3가지 항목을 디스플레이 장비에서 갱신해 가면서 보여주는 애플리케이션을 제작하려고 한다. 아래는 온도, 습도, 기압의 값을 새로 받을 때마다 호출되는 measurementsChanged() 메서드이다. 

 

 

🖥️ WatherData.java

public class WeatherData {

public void measurementsChanged() {

    float temp = getTemperature(); //온도 가져오기
    float humidity = getHumidity(); //습도 가져오기
    float pressure = getPressure(); //기압 가져오기

	//디스플레이 갱신
    currentConditionsDisplay.update(temp, humidity, pressure);
    statisticsDisplay.update(temp, humidity, pressure);
    forecastDisplay.update(temp, humidity, pressure); 
    }
}

 

디스플레이를 구현하고 새로운 값이 들어올 때마다, 즉 measurementsChanged() 메서드가 호출될 때마다 WeatherData에서 디스플레이를 업데이트되도록 만들었다. 해당 코드는 문제점은 아래와 같다. 

 

  • 구체적인 구현에 맞춰서 코딩했기에 프로그램을 고치지 않고는 다른 디스플레이 항목을 추가하거나 제거할 수 없다.
  • 바뀌는 부분을 캡슐화하지 않았다. 

 

이를 옵저버 패턴을 적용시켜 수정해 보자

 

 

 

해결방안

 

  • Observer - 주제에서 옵저버에게 갱신된 정보를 전달하는 방법을 제공한다.
  • CurrentConditionsDisplay / statisticsDisplay / ForecastDisplay - WeatherData 객체로부터 얻은 측정값을 보여준다.

 

 

  • Subject(주제) : 옵저버를 등록. 삭제, 주제가 바뀌었을 때 변경 내용을 알리는 역할을 한다. 
package ObserverPattern;

public interface Subject {

    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

 

  • Observer(옵저버)

기상정보가 변경되었을 때 옵저버에게 전달되는 상태 값들을 의미한다.

package ObserverPattern;

public interface Observer {

    void update(float temperature, float humidity, float pressure);
}

 

  • DisplayElement
package ObserverPattern;

public interface DisplayElement {

    public void display();
}

 

  • WeatherData

weatherData가 최신으로 반영되면 갱신되었다고 measurementsChanged 메서드에 알린다. measurementsChanged 메서드는 갱신된 측정값을 옵저버에게 알린다. notifyObservers 메서드는 등록된 모든 옵저버에게 상태변화를 알려준다. 

package ObserverPattern;

import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {

    private float temperature;
    private float humidity;
    private float pressure;
    private List<Observer> observers;


    public WeatherData() {
        observers = new ArrayList<Observer>();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for(Observer observer: observers) {
            observer.update(temperature, humidity, pressure);
        }

    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setWeatherData(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        measurementsChanged();
    }

    public float getTemperature() {
        return this.temperature;
    }

    public float getHumidity() {
        return this.humidity;
    }

    public float getPressure() {
        return this.pressure;
    }
}

 

 

  • CurrentConditionsDisplay
package ObserverPattern.display;

import ObserverPattern.DisplayElement;
import ObserverPattern.Observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;

        display();
    }

    @Override
    public void display() {
        System.out.println("현재 상태:  온도 "+temperature+"F, 습도 "+humidity+"%");
    }
}

 

 

  • weatherStation

weatherData 객체를 생성하고, 디스플레이를 생성한 뒤 옵저버를 등록한다. setWeatherData을 통해 새로운 기상 측정값을 업데이트한다. 

package ObserverPattern;

import ObserverPattern.display.CurrentConditionsDisplay;
import ObserverPattern.display.StatisticsDisplay;

public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        Observer currentConditionsDisplay = new CurrentConditionsDisplay();
        Observer statisticsDisplay = new StatisticsDisplay();


        weatherData.registerObserver(currentConditionsDisplay);
        weatherData.setWeatherData(3, 5, 7);

        System.out.println("통계 디스플레이를 추가합니다.");
        weatherData.registerObserver(statisticsDisplay);
        System.out.println("기상 데이터가 업데이트 됩니다.");
        weatherData.setWeatherData(20, 30, 80);

        System.out.println("현재 상태 디스플레이를 제거합니다.");
        weatherData.removeObserver(currentConditionsDisplay);
        weatherData.setWeatherData(25, 30, 80);

    }
}

 

여기까지 코드는 WeatherData가 하나의 데이터만 갱신해도 되는 상황에서도 update 메서드에 모든 데이터를 보내도록 설정되어 있다. 하지만 풍속 같은 새로운 데이터가 추가된다면 대부분 update메서드에서 풍속 데이터를 쓰지 않더라도 모든 디스플레이에 있는 update메서드를 바꿔주어야 한다. 

 

따라서 옵저버의 update 메서드를 인자 없이 호출하도록 수정한다. 

// WeatherData.java
@Override
    public void notifyObservers() {
        for(Observer observer: observers) {
            observer.update();
        }

    }
    
    
 
 // Observer.java
 public interface Observer {

    void update();
}

 

  • CurrentConditionsDisplay

생성자에서 weatherData를 받은 후 getter 메서드를 사용하여 정보를 가져온다.

package ObserverPattern.display;

import ObserverPattern.DisplayElement;
import ObserverPattern.Observer;
import ObserverPattern.WeatherData;

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private WeatherData weatherData;

    public CurrentConditionsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
    }

    @Override
    public void update() {
        this.temperature = weatherData.getTemperature();
        this.humidity = weatherData.getHumidity();

        display();
    }

    @Override
    public void display() {
        System.out.println("현재 상태:  온도 "+temperature+"F, 습도 "+humidity+"%");
    }
}

 

 


참조 : 

https://msyu1207.tistory.com/entry/2%EC%9E%A5-%ED%97%A4%EB%93%9C%ED%8D%BC%EC%8A%A4%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4#toc-%EA%B8%B0%EC%83%81%20%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%85%98%20%EC%84%A4%EA%B3%84%ED%95%98%EA%B8%B0

 

2장. 헤드퍼스트 디자인 패턴 - 옵저버 패턴

안녕하세요 😀 유로띠 입니다 😉 헤드퍼스트 디자인패턴 TIL (Today I Learned) 3줄 요약 ✏️ 상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다. ✏️ 일대다의 관계를 정의 ✏️

msyu1207.tistory.com

 

'Computer Theory > Design partterns' 카테고리의 다른 글

디자인 패턴과 SOLID 원칙  (0) 2024.05.22
팩토리 패턴  (0) 2023.09.20
데코레이터 패턴  (0) 2023.09.18
전략 패턴  (0) 2023.08.30

'Computer Theory/Design partterns'의 다른글

  • 현재글 옵저버 패턴

관련글