////
Search

2장 - 옵저버 패턴

Created
2022/09/03 11:28
Tags
디자인패턴

옵저버 패턴이란?

옵저버패턴 클래스 다이어그램
신문사 + 구독자 = 옵저버 패턴
신문사는 주제(subject), 구독자를 옵저버(observer)라고 부른다.
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에 연락이 가고 자동으로 내용이 갱신되는 방식으로 다대다 의존성을 정의합니다.
옵저버 패턴은 보통 주제 인터페이스와 옵저버 인터페이스가 들어있는 클래스 디자인으로 구현합니다.
옵저버로 등록하거나 탈퇴하고 싶은 경우 Subject인터페이스에 있는 메소드를 사용합니다.
옵저버가 될 가능성이 있는 객체는 Observer인터페이스를 구현해야합니다.
주제의 상태가 바뀌었을 때 호출되는 update() 메소드만 구현
옵저버에는 데이터를 보내주는 푸시(push) 방식과 옵저버가 데이터를 가져오는 풀(pull) 방식으로 구분할 수 있습니다.
Push 방식의 경우 매게변수가 변경될 가능성이 있다.
Pull 방식의 경우 옵저버가 필요한 데이터를 가져올 수있기 때문에 더 느슨하게 결합될 수 있다.
대체로 옵저버가 필요한 데이터를 가져오도록 하는게 더 좋은 방법입니다.

느슨한 결합

느슨한 결합은 객체들이 상호작용 할 수는 있지만, 서로를 잘 모르는 관계를 의미합니다.
→ 느슨할 결합을 이용하면 유연성이 아주 좋아짐
주제는 옵저버가 특정 인터페이스를 구현한다는 사실만 알고있습니다.
새로운 옵저버가 추가될때도 주제는 신경쓸 필요없이 Observer인터페이스만 구현되어 있으면 됩니다.
주제나 옵저버를 다은 용도로 활용할 일이 있다고 해도 손쉽게 재활용이 가능합니다.
서로 느슨하게 결합되어 있으므로 주제나 옵저버 인터페이스를 구현한다는 조건만 만족한다면 어떻게 고쳐도 문제가 생기지 않습니다.

요구사항

Weather-O-Rama의 차세대 인터넷 기반 기상 스테이션 구축 시스템
기상정보 객체 다이어그램
호출되는 객체의 메소드
Weather-O-Rama와 계약을 체결하면 WeatherData 객체를 통해 현재 조건, 기상통계, 기상 예보 이 3가지 디스플레이에 갱신하며 보여주어야 함
기상 관측이 변경된다면 measurementsChanged() 가 호출된다.
다른 개발자가 새로운 디스플레이를 추가할 수 있고, 이후 화면에 몇가지의 항목들이 더 추가 될 수 있음

해결 방법 1 : 메소드 직접 호출

첫번째 해결 방법은 아주 단순하게 measurementsChanged() 메소드에 디스플레이 객체의 메소드를 직접호출하는 방법이다.
public class WeatherData { public void measurementsChanged() { float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); currentConditionsDisplay.update(temp, humidity, pressure); statisticDisplay.update(temp, humidity, pressure); forecastDisplay.update(temp, humidity, pressure); } }
Java
복사

단점

디스플레이 객체가 변경될 수도 있다.
바뀔 수 있는 부분은 캡슐화 해야한다.
구체적인 구현에 맞춰 코딩했으므로 프로그램을 고치지 않고는 다른 디스플레이 항목을 추가할 수 없다.
실행 중 디스플레이 객체를 더하거나 뺄수없다.

해결 방법 2 : 옵저버 패턴 적용

이제 옵저버 패턴을 적용해 느슨한 결합을 만족시키고, 확장성을 지니도록 변경해보겠습니다.
public interface Subject { void registerObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); }
Java
복사
주제가 되는 인터페이스
public interface Observer { // push 방식 void update(float temperature, float humidity, float pressure); // pull 방식 void update(); }
Java
복사
옵저버 인터페이스
public interface DisplayElement { void display(); }
Java
복사
디스플레이 인터페이스
public class WeatherData implements Subject { private List<Observer> observers; private float temperature; private float humidity; private float pressure; public WeatherData() { this.observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(observers.indexOf(o)); } @Override public void notifyObservers() { // 실질적인 옵저버의 호출을 담당 for (Observer observer : observers) { observer.update(temperature, humidity, pressure); } } float getTemperature() { return temperature; } float getHumidity() { return humidity; } float getPressure() { return pressure; } void measurementsChanged(){ notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } ... }
Java
복사
주제를 구현한 WeatherData 클래스
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); } // 푸시 방식을 경우 @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } // 풀 방식을 경우 @Override public void update() { this.temperature = weatherData.getTemperature(); this.humidity = WeatherData.getHumidity(); display(); } @Override public void display() { System.out.println("현재 상태: 온도 " + temperature + "F, 습도 " + humidity + "%"); } }
Java
복사
옵저버 디스플레이

장점

객체들간 느슨한 결합을 이뤄내었기 때문에 각자의 책임에만 집중하면 된다.
런타임 도중에도 옵저버 객체를 추가하거나, 삭제할 수 있다.
옵저버 객체도 주제 인터페이스를 구현한다면 주제이자 옵저버가 될 수 있다.

정리

옵저버 패턴은 객체들 사이에 일대다 관계를 정의합니다.
옵저버 패턴을 사용하면 주제가 데이터를 보내거나 옵저버가 데이터를 가져올 수 있지만 일반적으로 데이터를 가져오는게 더 옳은 방식입니다.
옵저버 패턴은 자주 사용되는 패턴으로 MVC를 배울때도 다시 거론됩니다.