1:N 의존 관계를 정의하는 pattern으로, 한개의 객체 상태가 변경될때, 의존관계에 있는 모든 객체들**이 자동으로 알림을 받고 상태를 갱신하는 패턴이다.
일종의 푸시(Push) 서비스
를 구현한다.
결과
: Loose Coupling, 확장성(누구든지 등록 가능)
Observable
또는 Publisher
Subscriber
기상 모니터링 시스템을 구축한다고 가정해보자.
public void measurementsChanged() {
float temp = getTemperature(); // 최신 측정값 가져오는 함수
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure); // 디스플레이 갱신
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
위의 코드에는 두 가지 문제점이 존재한다.
새로운 디스플레이 조건이 추가되어 시스템이 확장되면, 어떻게 기존 코드를 안건드리고 유연하게 대처할 수 있을까?
Observer Pattern을 사용하면, 주제와 구독자 간 느슨한 결합을 생성해낼 수 있다.
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers(); // 상태가 변경되면 옵저버들에게 연락
}
푸쉬를 받고자 하는 사용자가 registerObserver
를 통해 등록되면, 특정 상황이 발생했을 때(Event) notifyObservers()
를 통해 등록된 사용자에게 모두 알린다.
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
public void display();
}
public class WeatherData implements Subject {
private ArrayList<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) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
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 class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData; //WeatherData 가 아닌 Subject(중요!)
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this); //Subject에 등록 요청
}
public void update(float temperature, float humidity, float pressure) { //Subject에서 호출됌
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Current conditions: "
+ temperature + "F degrees and " + humidity
+ "% humidity");
}
}
현재 주제(Subject)에서 데이터들을 구독자들에게 일방적으로 전송하고 있다. 하지만, 만약 디스플레이에 보여줄 요소가 증가하면 update
메서드에 변경이 생기고 이는 모든 디스플레이 있는 update 메서드의 변경을 야기한다.
따라서, 구독자쪽에서 필요한 데이터를 골라서 가져가도록 하는 방법이 더 좋다. 단순히 게터 메서드만 추가해주면 된다.
public interface Observer {
public void update();
}
public void update() {
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidty();
display();
}
PUB-SUB
패턴과 다른 개념이다. 구독자가 서로 다른 유형의 메시지에 관심을 가질 수 있고, 출판사와 구독자를 더 세세하게 분리할 수 있는 패턴
서비스간 의존성을 낮추기 위해..
https://sevrain-chea.medium.com/how-to-implement-observer-pattern-using-springboot-events-106d80b9ea05
https://m.blog.naver.com/gngh0101/221337447578