Design Pattern - Observer Pattern

겔로그·2022년 9월 11일
0

Design Pattern

목록 보기
3/7
post-thumbnail

개요

오늘은 옵저버 패턴에 대해 알아볼 계획이다. Head First Design Pattern에서는 75p ~ 116p의 부분이며 구글링한 정보와 종합하여 요약 및 정리할 계획이다.

오늘은 먼저 문제상황을 제시하고 옵저버 패턴이 무엇인지, 어떻게 적용할 것인지 순서대로 진행해 보겠다.

문제 - 기상 관측

우리는 WeatherData 객체를 사용하여 다음 애플리케이션을 만들어야 한다.

요구사항

요구사항은 기상 스테이션에서 데이터를 수집해 디스플레이 화면에 보여주는 것이다.
데이터 수집 및 디스플레이 화면에서 보여줘야 될 내용은 다음과 같다.

  • 기상 스테이션에서 데이터 수집
    • 습도 센서
    • 온도 센서
    • 압력 센서
  • 디스플레이 화면
    - 현재 조건
    - 기상 통계
    - 기상 예측

가장 먼저 데이터 수집을 진행하는 Weather Data를 볼 경우 다음과 같다.

현재 알고 있는 정보 및 요구사항 정리

  1. Weather Data에서는 다음 메소드를 제공한다.
    • 온도, 습도, 기압의 getter 메소드
  2. 새로운 기상 측정 데이터가 나올 때마다 measurementsChanged();를 호출한다.
  3. 세 개의 디스플레이 화면을 구현해야 한다. (조건 표시, 기상 통계, 기상 예보)
  4. 시스템이 확장 가능해야 한다. (디스플레이 항목 추가/삭제가 가능해야 함)

무지성 코딩

요구사항을 정리해 보면 기본적으로 데이터가 올 경우, measurementsChanged(); 메소드가 호출되기만 하면 디스플레이에 정보를 나타낼 수 있기 때문에 Weather Data의 measurementsChanged();만 잘 짜면 되지 않을까 하는 생각이 들었다.

    public void measurementsChanged() {
        float tem = getTemperature();
        float hum = getHumidity();
        float press = getPressure();
        
        display1.update(tem,hum,press); // 화면 1 변경
        display2.update(tem,hum,press); // 화면 2 변경
        display3.update(tem,hum,press); // 화면 3 변경
    }

measurementsChanged(); 메소드를 보았을 때 발생하는 문제점은 다음과 같다.

  • 구체적인 구현에 알맞게 코딩하여 프로그램을 고치지 않고서는 다른 디스플레이 항목을 추가/제거할 수 없다.
  • 모든 디스플레이가 동일하게 온도,습도,압력 정보를 받아 update하는 것을 알 수 있다. 해당 부분을 캡슐화 해야 한다.

Observer Pattern이란?

  • 한 객체의 상태가 바뀔 경우 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 상태가 갱신되는 방식

옵저버 패턴의 특징

  • 일대다 관계는 주제와 옵저버에 의해 정의된다.
  • 옵저버는 주제에 의존한다.
  • 주제의 상태가 바뀌면 옵저버한테 연락이 간다.

옵저버 패턴 클래스 다이어그램

다음과 같은 구조를 이용하면 아래와 같은 이점을 얻는다.

  • 주제(Subject)는 옵저버는 특정 인터페이스를 구현한다라는 정보만 안다.
    - 구상 클래스가 무엇이고 무엇을 하는지 알 필요가 없다.
    • 다음과 같은 특징으로 변경에 있어 유연하다. (주제와 옵저버는 서로에게 무신경함)
  • 옵저버는 언제든지 추가/제거할 수 있다.
  • 주제와 옵저버는 서로 독립적으로 재사용할 수 있다.
  • 주제와 옵저버가 변경되어도 서로한테 영향을 미치지 않는다.

=> 옵저버 패턴은 매우 느슨한 결합을 가지는구나!를 알 수 있다.

그래서 이걸 어떻게 적용시킬건데..?

천천히 생각해보자.
현재 주제(Subject)는 무엇이고 옵저버로 넣을 수 있는 요소는 어떤 것일까?

  • 주제(Subject): WeatherData
  • 옵저버(Observer): Display

클래스 다이어그램으로 관계를 표현하면 다음과 같을 것이다.

여기에 만약 현재 정보 화면을 추가하고 싶다고 하면 다음과 같은 클래스가 추가될 것이다.

시각적으로 보니 이해가 되나? 물론.. 현재 정보를 그대로 보여주는 CurrentDisplay클래스는 그냥 WeatherData 상속해서 보여줄 수도 있지만 나는 해당 화면도 삭제할 수 있도록 다음과 같이 정의하였다.

자! 그럼 이제 코드로 정리해보자.

interface(Subject, Observer, Display)


public interface Subject {
    public void addObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

public interface Display {

    public void display();
}

public interface Observer {
    public void update(float temp, float humidity, float preesure);
}

WeatherData.java

public class WeatherData implements Subject {
    ArrayList<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;
    private float pressure;

    private Display display1;
    private Display display2;
    private Display display3;

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

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

    public float getTemperature() {
        return temperature;
    }

    public void measurementsChanged() {
        notifyObservers();
    }

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

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i >= 0) {
            observers.remove(i);
        }
    }

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

CurrentDisplay.java

public class CurrentDisplay implements Observer, Display{
    private Subject weatherData;
    private float temperature;
    private float humidity;

    public CurrentDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.addObserver(this);
    }

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

    @Override
    public void display() {
        System.out.println("Current Condition: "+ temperature + "`C degrees and " + humidity +"% humidity.");
    }
}

main.java

public class main {
    public static void main(String[] args) throws IOException {
        WeatherData weatherData = new WeatherData();
        Display display = new CurrentDisplay(weatherData);
        weatherData.setWeatherData(30,20,40);
    }
}

Display display = new CurrentDisplay(weatherData);를 통해 Display가 WeatherData 인스턴스의 Observer List에 등록이 되며, 다음과 같이 setWeatherData메소드를 통해 설정값이 변경되었을 경우 display가 실행된다.

결과

결론

오늘은 Observer Pattern에 대해 알아보는 시간을 가졌다.

처음에는 되게 어려워 보였는데 유투브의 구독 시스템을 생각하면서 정리하다보니 이해가 좀 더 수월했던 것 같다. 결국 구독자와 유투버는 서로 구독/구독 취소를 한다고 해서 서로 영향을 받지 않으며 유투버가 영상을 올릴경우 구독자들에게 해당 영상이 업로드 되었다고 알람이 가니 현실 상황에 대입해 생각해본다면 옵저버 패턴을 좀 더 수월하게 이해할 수 있을 것이다.

아직은 이러한 패턴을 사용할 곳은 없었지만.. 언젠가는 사용할법한 보편적인 패턴 같은 느낌이 들었던 것 같다.

profile
Gelog 나쁜 것만 드려요~

0개의 댓글