Subject에 여러 Observer를 등록해 두고, Notify를 하게 되면 루프를 돌면서 각 Observer를 Update하는 패턴이다.
Subject
(인터페이스 or 추상 클래스)add
: Subject에 Observer를 구독자로 등록한다.remove
: Subject에 등록한 Observer의 구독을 해지한다.notify
: Subject에서 모든 Observer에 정보를 전달한다.Observer
(인터페이스 or 추상 클래스)ConcreteSubject
(Subject 구현 클래스)ConcreteObserver
(Observer 구현 클래스)update
: Observer에서 오버라이드해서 사용날씨 분석 시스템
Subject
Observer
temp
humidity
pressure
currentConditionsDisplay
forecastDisplay
statisticsDisplay
WeatherData (기상청 데이터)
class WeatherData {
getTemperature()
getHumidity()
getPressure()
measurementsChanged()
}
Display (화면에 표시할 것)
public void measurementsChanged(){ // 기상 관측값이 갱신되면 호출
// 바뀐 기상 측정값
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
// 디스플레이 화면 업데이트
currentConditionDisplay.update(temp, humidty, pressure);
forecastDisplay.update(temp, humidty, pressure);
statisticsDisplay.update(temp, humidty, pressure);
WeatherData
의 변화-> Display
내용이 갱신 ⭐디자인원칙: 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
interface Subject {
registerObserver() // 옵저버 등록
removeObserver() // 옵저버 삭제
notifyObserver() // 옵저버에게 업데이트 알림
}
class WeatherData implements Subject {
registerObserver()
removeObserver()
notifyObserver()
// 상태를 설정하고 알기위한 getter, setter
getTemperature()
getHumidity()
getPressure()
measurementsChanged()
}
interface Observer {
update() // 새로 갱신된 주제 데이터를 전달하는 인터페이스
}
interface DisplayElement {
display() // 화면에 표현시키는 인터페이스
}
class CurrentConditionsDisplay implements Observer, DisplayElement{
update()
display() { // 현재 측정 값을 화면에 표시 }
}
class ForecastDisplay implements Observer, DisplayElement{
update()
display() { // 날씨 예보 표시 }
}
class StatisticsDisplay implements Observer, DisplayElement{
update()
display() { // 평균 기온, 평균 습도 등 표시 }
}
Subject (Interface)
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
Observer (Interface)
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
public void display();
}
ConcreteSubject (Subject를 구현하는 클래스)
public class WeatherData implements Subject {
private ArrayList observers; // Generics: ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList(); // Generics: 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 (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
// Generics: Observer observer = observers.get(i);
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 float getTemperature() {return temperature;}
public float getHumidity() {return humidity;}
public float getPressure() {return pressure;}
}
ConcreteObserver
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) { // Subject라는 인터페이스를 구현한 어떤 클래스가 들어와도 똑같이 동작
this.weatherData = weatherData;
weatherData.registerObserver(this); // 나 자신을 weatherData에
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
}
}
public class ForecastDisplay implements Observer, DisplayElement {
private float currentPressure = 29.92f;
private float lastPressure;
private WeatherData weatherData;
public ForecastDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
lastPressure = currentPressure;
currentPressure = pressure;
display();
}
public void display() {
System.out.print("Forecast: ");
if (currentPressure > lastPressure) {
System.out.println("Improving weather on the way!");
} else if (currentPressure == lastPressure) {
System.out.println("More of the same");
} else if (currentPressure < lastPressure) {
System.out.println("Watch out for cooler, rainy weather");
}
}
}
public class StatisticsDisplay implements Observer, DisplayElement {
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum= 0.0f;
private int numReadings;
private WeatherData weatherData;
public StatisticsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
tempSum += temp;
numReadings++;
if (temp > maxTemp) { maxTemp = temp;}
if (temp < minTemp) { minTemp = temp;}
display();
}
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);
}
}
Main
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
weatherData.removeObserver(forecastDisplay);
weatherData.setMeasurements(62, 90, 28.1f);
}
}
"느슨하게 결합되어 있다" 라는 것은 결합되어있는 둘이 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 뜻이다.
Subject
와 Observer
는 느슨한 결합 관계이다.
= Subject가 Observer에 대해 아는 것은 특정 인터페이스를 구현한다는 것뿐, Observer를 구현하는 실제 클래스와 역할은 모른다.