Observer Pattern
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로, 일대다 의존성을 정의한다.
- Subject에서 중요한 데이터를 관리하고, Subject의 상태가 변화하면 Observer에서 소식이 전해진다.
- Observer는 Subject를 구독하고 있으며 갱신 내용을 전달 받는다.
OO 원칙
상호작용하는 객체 사이에는 가능하면 느슨한 결합을 사용해야 한다.
느슨한 결합
객체 사이의 상호의존성을 최소화한다.
- Subject는 Observer가 특정 인터페이스를 구현한다는 사실만 안다.
- 언제든지 Observer를 추가 가능하다.
- 새로운 형식의 Observer를 추가할 때마다 Subject를 수정할 필요가 없다.
- Subject와 Observer는 서로 독립적으로 재사용 가능하다.
- Subject와 Observer는 달라져도 서로 영향을 미치지 않는다.
기상 스테이션
- 기존 코드
public class WeatherData {
// 인스턴스 변수 선언
public void measurementChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastConditionDisplay.update(temp, humidity, pressure);
}
}
문제점 : 새로운 기상 측정 데이터가 들어올 때마다 디스플레이를 업데이트해야한다.
코드 구현
- 바뀌는 부분: Subject의 상태, Observer의 개수, 형식
- 추가되는 Display를 Observer로 하고, 이를 등록, 삭제, 소식 전달을 할 Subject 클래스를 생성한다.
- Observer마다 바뀌는 Display 요소를 캡슐화한다.
- Composition 관계
Subject와 Observer 인터페이스 생성
- Subject 인터페이스는 Observer을 등록, 삭제하는 메서드가 있다.
- Objserver 인터페이스는 Subject의 상태가 바뀌었을 때 호출되는 update 메서드가 존재한다.
public interface Subject{
//Observer를 인자로 받아 등록, 삭제
public void registerObserver(Observer o);
public void removeObserver(Observer o);
// Observer에게 상태 변화를 알림
public void notifyObserver(Observer o);
}
public interface Observer{
// Subject로 부터 상태변화를 받음
public void update();
}
public interface Display{
public void display();
}
Concrete Subject 생성
- Subject 인터페이스를 구현한다.
- Observer들을 저장하고, 업데이트 된 정보를 Observer에게 알린다.
public class WeatherData implements Subject {
private List<Observer> observers; // observer 객체를 저장할 리스트
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<Observer>();
}
public void registerObserver(Observer o) {
observers.add(o); // observer 등록
}
public void removeObserver(Observer o) {
observers.remove(o); // observer 제거
}
public void notifyObservers() {
// 모든 observer에게 상태 변화를 알려줌
for (Observer observer : observers) {
observer.update();
}
}
public void measurementsChanged() {
// observer에게 값 전달
notifyObservers();
}
// 값 갱신
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temparture;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
//변수 get 메서드
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
Concrete Observer 생성
- Subject에 등록할 Observer을 생성한다.
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private WeatherData weatherData;
public CurrentConditionsDisplay(WeatherData weatherData) {
this.weatherData = weatherData; // 구독할 Subject를 받음
weatherData.registerObserver(this); // Observer로 등록
}
public void update() {
// Subject로부터 갱신된 정보 중 필요한 정보만을 받음
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "C degrees and " + humidity + "% humidity");
}
}
...
Test.java
public class WeatherStation {
public static void main(String[] args) {
// subject 생성
WeatherData weatherData = new WeatherData();
// observer 등록
CurrentConditionsDisplay currentDisplay =
new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
// 값 변경 후 모든 observer 에게 전달
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
// observer 해지
weatherData.removeObserver(forecastDisplay);
weatherData.setMeasurements(62, 90, 28.1f);
}
}
적용
Swing 라이브러리
- Swing: 사용자 인터페이스 용도의 자바 GUI 툴킷
- 다양한 유형의 이벤트를 감시하는 옵저버(리스너)를 추가하거나 제거하는 메서드들이 존재한다.
- ex) JButton: 슈퍼클래스인 AbstractButton에는 리스너를 추가하고 제거하는 메서드가 존재한다.
ActionListener: 버튼 이벤트를 감시하고, 이벤트 변화가 발생했을 때 적용할 기능을 구현할 수 있다.
MVC 패턴
- Model(주체)에서 변경 사항이 생겨 update() 메서드로 옵저버인 View에게 알려주고 이를 기반으로 Controller 등이 동작한다.
proxy 객체
- proxy 객체: 어떤 대상의 기본적인 동작의 작업을 가로챌 수 있는 객체
- target(프록시할 대상), handler(프록시 객체의 target 동작을 가로채서 정의할 동작들이 정해져 있는 함수)
- 접근을 가로채서 상태 변화를 감시한다.
Vue.js 3.0
- ref나 reactive로 정의하면 값 변경시 자동으로 DOM(웹 브라우저 화면을 이루고 있는 요소)에 있는 값이 변경된다.
- ref는 변수를 반응성 데이터로 만든다. ref로 선언된 변수의 값이 변경되면 해당 값을 사용하는 Vue 컴포넌트의 뷰도 자동으로 업데이트된다.
- proxy 객체의 옵저버패턴을 이용하여 구현한 것이다.
'Books > Head First Design Pattern' 카테고리의 다른 글
| [6장] Command Pattern (0) | 2024.05.24 |
|---|---|
| [5장] Singleton Pattern (0) | 2024.05.24 |
| [4장] Factory Pattern (0) | 2024.05.24 |
| [3장] Decorator Pattern (0) | 2024.05.24 |
| [1장] Strategy Pattern (0) | 2024.05.24 |