본문 바로가기

공부방/JAVA

디자인 패턴 - 옵저버 패턴

0. 옵저버 패턴?


옵저버 패턴은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 
자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의합니다.

1. 용어 정리


옵저버 패턴에서는 크게 두가지 용어를 먼저 알고 가면 될 것 같습니다.

 

주제 (subject): 데이터와 옵저버를 관리하는 객체

옵저버 (observer): 데이터가 변경될 경우, 주제로 부터 변경사항을 전달받는 객체

2. 예시로 확인하는 옵저버 패턴


1. 예시에서 Subject는 Observer 3개를 관리하는 형태입니다.

2. Subject는 관리하고 있는 data가 존재하고 있습니다. (data1, data2, data3)

3. Observer도 관리하고 있는 data가 존재합니다. (예시에서는 observer마다 관리하고 있는 data의 종류가 다릅니다.)

 

 

4. data 변경 event가 발생하면, 변경된 data 정보는 subject로 전달됩니다.

(검은색 이었던 data1이 빨간색 data1로 변경됨, 값이 변경되었다고 생각하시면 됩니다.)

 

 

5. subject는 관리하고 있는 observer 들에게 data1이 변경되었다는 정보를 전달합니다.

6. observer들은 변경된 data값을 subject로 부터 전달받고, 저장합니다.

 

 

3.코드로 확인하는 옵저버 패턴 


3.1 주제 (subject)

주제란 데이터와 옵저버를 관리하는 객체라고 하였습니다.

 

주제가 옵저버를 관리하는 기능을 interface로 분리하였습니다.

옵저버를 등록하는 registerObserver,

옵저버를 제거하는 removeObserver,

변경된 사항을 옵저버에게 알려주는 notifyObservers가 존재합니다.

 

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

 

주제(Subject)의 구현체에서는 

옵저버를 관리하는 기능에 대하여 구현을 함과 동시에

관리하는 data (temperature, humidity, pressure)를 필드 필드변수로 선언합니다.

 

package headfirst.designpatterns.observer.weather;

import java.util.*;

public class WeatherData implements Subject {
   private List<Observer> observers;
   private float temperature;
   private float humidity;
   private float pressure;
   
   public WeatherData() {
      observers = new ArrayList<Observer>();
   }
   
   public void registerObserver(Observer o) {
      observers.add(o);
   }
   
   public void removeObserver(Observer o) {
      observers.remove(o);
   }
   
   public void notifyObservers() {
      for (Observer observer : observers) {
         observer.update(temperature, humidity, pressure);
      }
   }
   
   public void measurementsChanged() {
      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;
   }

}

 

옵저버


데이터가 변경될 경우, 주제로 부터 변경사항을 전달받는 객체를 옵저버라고 합니다.

주제로 부터 변경된 데이터의 정보를 전달 받는 기능을 interface로 분리합니다.

 

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

 

옵저버가 값이 변경되면, update된 내역을 출력하는 로직에 대한 interface도 만들어 보겠습니다.

 

public interface DisplayElement {
   public void display();
}

 

Observer interface와 DisplayElement interface의 구현체인 CurrentConditiondisplay를 만들어 보겠습니다.

생성자에서 weatherData (Subject)를 받아서 자기 자신을 옵저버로 등록을 합니다.

update 메서드에서 변경된 내역을 subject로 부터 받고, 변경된 정보를 저장하고 출력하는 메서드 (display)를 호출합니다.

 

public class CurrentConditionsDisplay implements Observer, DisplayElement {
   private float temperature;
   private float humidity;
   private WeatherData weatherData;
   
   public CurrentConditionsDisplay(WeatherData weatherData) {
      this.weatherData = weatherData;
      weatherData.registerObserver(this);
   }
   
   public void update(float temperature, float humidity, float pressure) {
      this.temperature = temperature;
      this.humidity = humidity;
      display();
   }
   
   public void display() {
      System.out.println("Current conditions: " + temperature 
         + "F degrees and " + humidity + "% humidity");
   }
}

 

Main 메서드


아래 main 메서드를 살펴보면 4개의 옵저버를 호출 하고 있습니다.

(currentConditionsDisplay, StatisticsDisplay, ForecastDisplay, HeatIndexDisplay)

 

우리는 그 중에서 1개만 구현을 해보았는데, 추가적으로 3개의 옵저버를 만들어서 아래 메인 메서드를 완성시켜보면 좋을 것 같습니다.

 

public class WeatherStation {

   public static void main(String[] args) {
      WeatherData weatherData = new WeatherData();
   
      CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
      // StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
      // ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
      // HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

      weatherData.setMeasurements(80, 65, 30.4f);
      System.out.println();

      weatherData.setMeasurements(82, 70, 29.2f);
      System.out.println();

      weatherData.setMeasurements(78, 90, 29.2f);
      System.out.println();

      weatherData.removeObserver(forecastDisplay);
      weatherData.setMeasurements(62, 90, 28.1f);
   }
}

 

1. 옵저버를 생성합니다.

    - 매개변수로 주제를 넣어줍니다.

    - 옵저버 생성자에서는 주제에 옵저버를 넣어주는 로직이 들어가 있기 때문에

    - 아래 코드를 통하여 주제는 해당 옵저버를 관리하게 됩니다.

 

CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

 

2. 데이터의 변경이 일어났습니다.

    - 주제는 옵저버에게 데이터 변경이 일어났다고 알려줄 예정입니다.

    - 옵저버는 주제에게 데이터 변경 됐다는 정보를 받고, 변경된 데이터를 저장한 뒤 결과를 출력합니다.

 

weatherData.setMeasurements(80, 65, 30.4f);

 

3. 옵저버는 더이상 새로운 정보를 받지 않겠다고 선언하였습니다.

    - 주제는 더이상 해당 옵저버를 관리하지 않습니다.

 

weatherData.removeObserver(forecastDisplay);

 

4. 데이터 변경이 일어났습니다.

    - 주제는 앞서 제외된 옵저버 외에 나머지 옵저버는 변경된 데이터 내역을 전달합니다.

    - 옵저버는 주제에게 데이터 변경 됐다는 정보를 받고, 변경된 데이터를 저장한 뒤 결과를 출력합니다.

 

weatherData.setMeasurements(62, 90, 28.1f);

결과 확인


출력되는 결과는 아래와 같습니다.

 

 

'공부방 > JAVA' 카테고리의 다른 글

Decorator pattern  (0) 2022.07.29
Consumer  (0) 2022.07.28
디자인 패턴 - strategy pattern  (0) 2022.07.11
mysql - 2일차  (0) 2022.07.08
mysql  (0) 2022.07.07