옵저버 패턴

Lee yun bok·2021년 9월 1일
0
post-thumbnail

옵저버 패턴이란?

옵저버 패턴은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 내용이 갱신되는 일대다 의존성을 정의한다.


pressure, temperature, humidity를 출력하는 Display를 구현해보자

요구사항

  1. WeaterData라는 클래스는 어디선가 pressure, temperature, humidity 정보를 실시간으로 가져오는 기능을 가지고 있다.
  2. 새로운 기상 측정 데이터가 생길 때 마다 뷰의 데이터가 변경되어야 한다.
  3. 디스플레이는 여러 종류가 있다.
  4. 여러가지의 디스플레이를 손쉽게 확장할 수 있어야 한다.

좋지 않는 구현 방식

public class WeatherData {

    public void measurementChanged() {
        float temperature = getTemperature(); 
        float humidity = getHumidity();  
        float pressure = getPressure(); 

        currentConditionDisplay.update(temperature, humidity, pressure); 
    }
}
  1. 동작은 한다, 그런데 만약에 새로운 Display가 추가되면? 또는 Display가 제거된다면? WeatherData를 수정해야한다.
  2. 만약에 몇십개의 Display가 추가된다면 일일히 몇십 개의 Display를 위한 update를 추가적으로 호출해야 한다.
  3. 굉장히 유지보수적으로 좋지 않다.

위의 코드는 확장성에 알맞지 않은 코드라고 할 수 있다. 그렇다면 어떤식으로 구성을 해야 확장성을 갖춘 코드를 구현할 수 있을까? 요구사항의 중점은 수많은 구성의 화면이 추가 될 여지가 있어 확장성이 중요하다는 것이다. 그렇다면 확장성 있는 코드를 작성하기 위해서는 결국 WeaterDataDisplay 사이의 관계가 유연해야 한다. 이때 Display(옵저버)와 WeaterData(주제) 사이의 관계를 느슨하게 해줄 수 있는 것이 바로 옵저버패턴이다.

옵저버 패턴을 적용한 구현 방식

//== 옵저버들이 구현할 인터페이스 ==// 
public interface Observer {

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

//== 주제들이 구현할 인터페이스 ==//
public interface Subject {

    void registerObservers(Observer o); // 옵저버를 추가한다.
    void removeObservers(Observer o); // 옵저버를 제거한다.
    void notifyObservers(); // 옵저버에게 알린다.
}

//== Display 기능을 담고 있는 인터페이스 ==// 
public interface DisplayElement {

    void display();
}
  1. Observer들은 SubjectregisterObservers()removeObservers()를 통해 구독하거나 해지한다.
  2. Subject는 구독한 Observer들에게 notifyObservers()를 통해 변경을 통지한다.
public class WeatherData implements Subject{

    private ArrayList<Observer> observers;
    private float temperatures;
    private float humidity;
    private float pressure;

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

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

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

    @Override
    public void notifyObservers() {
        for(Observer o : observers) {
            o.update(this.temperatures, this.humidity, this.pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperatures, float humidity, float pressure) {
        this.temperatures = temperatures;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
  1. Subject를 구현한 WeaterData 구현 클래스이다.
  2. Observer들이 registerObservers()removeObservers()를 호출하면 observers 리스트에 넣거나 제거한다.
  3. 데이터가 변경되면 observers 리스트에 있는 observer들에게 변경을 통지한다.
  4. observer들은 Observer 인터페이스를 구현했기 때문에 단순히 WeaterDataupdate()만 호출하면 된다.
public class CurrentConditionsDisplay implements Observer, DisplayElement{
    private float temperature;
    private float humidity;
    private Subject weatherData;


    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObservers(this);
    }

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

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}
  1. Observer를 구현한 Display의 한 종류인 CurrentConditionDisplay이다.
  2. 생성자에서 weatherDataregisterObservers()를 호출함으로써 Subject를 구독한다.
  3. Observer에서 호출할 update() 메서드를 자신의 로직에 맞게 구현한다.

출처
Head First Design Patterns - (에릭 프리먼, 엘리자베스 프리먼, 케이시 시에라, 비트 베이츠) 을 보며 정리한 내용입니다.

profile
https://ybdeveloper.tistory.com/ 로 이동했습니다 : )

0개의 댓글