오늘은 옵저버 패턴에 대해 알아볼 계획이다. Head First Design Pattern에서는 75p ~ 116p의 부분이며 구글링한 정보와 종합하여 요약 및 정리할 계획이다.
오늘은 먼저 문제상황을 제시하고 옵저버 패턴이 무엇인지, 어떻게 적용할 것인지 순서대로 진행해 보겠다.
우리는 WeatherData 객체를 사용하여 다음 애플리케이션을 만들어야 한다.
요구사항은 기상 스테이션
에서 데이터를 수집해 디스플레이 화면
에 보여주는 것이다.
데이터 수집 및 디스플레이 화면에서 보여줘야 될 내용은 다음과 같다.
가장 먼저 데이터 수집을 진행하는 Weather Data를 볼 경우 다음과 같다.
measurementsChanged();
를 호출한다.요구사항을 정리해 보면 기본적으로 데이터가 올 경우, 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();
메소드를 보았을 때 발생하는 문제점은 다음과 같다.
의존
하는 다른 객체들한테 연락이 가고 자동으로 상태가 갱신되는 방식옵저버 패턴의 특징
의존
한다.다음과 같은 구조를 이용하면 아래와 같은 이점을 얻는다.
옵저버는 특정 인터페이스를 구현한다
라는 정보만 안다.=> 옵저버 패턴은 매우 느슨한 결합을 가지는구나!를 알 수 있다.
천천히 생각해보자.
현재 주제(Subject)는 무엇이고 옵저버로 넣을 수 있는 요소는 어떤 것일까?
클래스 다이어그램으로 관계를 표현하면 다음과 같을 것이다.
여기에 만약 현재 정보
화면을 추가하고 싶다고 하면 다음과 같은 클래스가 추가될 것이다.
시각적으로 보니 이해가 되나? 물론.. 현재 정보를 그대로 보여주는 CurrentDisplay
클래스는 그냥 WeatherData 상속해서 보여줄 수도 있지만 나는 해당 화면도 삭제할 수 있도록 다음과 같이 정의하였다.
자! 그럼 이제 코드로 정리해보자.
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);
}
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);
}
}
}
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.");
}
}
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에 대해 알아보는 시간을 가졌다.
처음에는 되게 어려워 보였는데 유투브의 구독 시스템을 생각하면서 정리하다보니 이해가 좀 더 수월했던 것 같다. 결국 구독자와 유투버는 서로 구독/구독 취소를 한다고 해서 서로 영향을 받지 않으며 유투버가 영상을 올릴경우 구독자들에게 해당 영상이 업로드 되었다고 알람이 가니 현실 상황에 대입해 생각해본다면 옵저버 패턴을 좀 더 수월하게 이해할 수 있을 것이다.
아직은 이러한 패턴을 사용할 곳은 없었지만.. 언젠가는 사용할법한 보편적인 패턴 같은 느낌이 들었던 것 같다.