한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로, 일대다 의존성을 정의한다.
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 인터페이스 생성
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 생성
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 생성
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);
}
}