옵저버 패턴이란?
옵저버패턴 클래스 다이어그램
신문사 + 구독자 = 옵저버 패턴
•
신문사는 주제(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를 배울때도 다시 거론됩니다.