[디자인패턴] 옵저버 패턴
[디자인패턴] 옵저버 패턴
옵저버 패턴
옵저버 패턴이란
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체에게 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(One To Many) 의존성을 정의한다.
일대다 관계는 느슨한 결합으로 구현한다.
클래스 다이어그램
classDiagram
Subject <|.. WeatherData
Subject --> Observer: Subject의 데이터 update
Observer <|.. CurrentConditionDisplay
Observer <|.. StaticsDisplay
Observer <|.. ForcastDisplay
Display <|.. CurrentConditionDisplay
Display <|.. StaticsDisplay
Display <|.. ForcastDisplay
WeatherData *-- CurrentConditionDisplay: 구현된 Subject 구독
WeatherData *-- StaticsDisplay: 구현된 Subject 구독
WeatherData *-- ForcastDisplay: 구현된 Subject 구독
class Subject {
<<interface>>
registerObserver()
removeObserver()
notifyObserver() 모든 옵저버의 상태 업데이트
}
class WeatherData {
registerObserver()
removeObserver()
notifyObserver()
getTemperature()
getHumidity()
getPressure()
measurementsChanged()
}
class Observer {
<<interface>>
update()
}
class Display {
<<interface>>
display()
}
class CurrentConditionDisplay {
update()
display() 측정값
}
class StaticsDisplay {
update()
display() 평균, 최저, 최고값
}
class ForcastDisplay {
update()
display() 기상 예보
}
구성 및 동작원리
옵저버 등록
Subject에서 옵저버를 컨트롤 할 수 있도록 registerObserver(Observer observer) 메서드를 오버라이드하여 구현한다.
옵저버의 객체 생성 시, 생성자에 Subject(WeahterData)에 등록하여(리스트에 담아) 해당 옵저버가 Subject의 옵저버로 추가한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Subject 인터페이스 구현체
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
// weather 데이터가 생성시 옵저버를 등록할 수 있도록 리스트 초기화
public WeatherData() {
this.observers = new ArrayList<>();
}
// 옵저버를 Subject에 등록하는 메서드
@Override
public void registerObserver(Observer observer) {
this.observers.add(observer);
}
//..
}
// Observer 인터페이스 구현체
public class CurrentConditionDisplay implements Observer, Display {
private float temperature;
private float humidity;
private WeatherData weatherData;
public CurrentConditionDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this); // WeatherData(Subject)에 등록
}
// ...
}
Subject에 구독된 옵저버에게 메시지 전달
Subject를 상속받은 WeatherData는 Observer들에게 정보를 한번에 보내줘야한다.
여기서는 setMeasurements를 호출하여 데이터를 저장하는 동시에 새로운 데이터를 모든 Observer들에게 보내주기위해 모든 옵저버들의 update 메서드를 호출한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// WeatherData
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
/*
weather 데이터가 생성시 옵저버를 등록할 수 있도록
리스트 초기화
*/
public WeatherData() {
this.observers = new ArrayList<>();
}
// 옵저버 등록
@Override
public void registerObserver(Observer observer) {
this.observers.add(observer);
}
/*
기상 정보가 변경되면 모든 옵저버의 상태를 변경
*/
@Override
public void measurementsChanged() {
this.notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this.temperature, this.humidity, this.pressure);
}
}
}
옵저버 인터페이스를 상속받은 옵저버 구현 글래스들은 update() 메서드를 해당하는 클래스의 기능별로 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// CurrentConditionDisplay
public class CurrentConditionDisplay implements Observer, Display {
private float temperature;
private float humidity;
private WeatherData weatherData;
public CurrentConditionDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this); // WeatherData(Subject)에 등록
}
/*
update 호출시 기온, 습도를 변경하고 display를 호출한다.
*/
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("temperature = " + temperature);
System.out.println("humidity = " + humidity);
}
}
객체 생성
WeatherData(Subject) 생성하여 각 Observer들을 등록한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Executor
public class Executor {
public static void main(String[] args) {
// subject 객체 생성
WeatherData weatherData = new WeatherData();
// 옵저버 객체 생성 후 subject 객체를 인자로 전달
CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
StaticDisplay staticDisplay = new StaticDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
// weatherData의 데이터를 변경하여 옵저버의 상태를 업데이트
weatherData.setMeasurements(50, 80, 30.4f);
weatherData.setMeasurements(60, 80, 29.4f);
weatherData.setMeasurements(70, 65, 28.4f);
}
}
풀 방식으로 변경하기
지금까지 옵저버 패턴은 Subject가 옵저버를 업데이트 하는 방식이었다면 옵저버가 필요할 때마다 Subject의 데이터를 폴링하는 방식으로도 변경할 수 있다.
- 옵저버의 update() 메서드의 매개변수를 지워준다.
- 옵저버 호출 시, 필드로 선언된 포함(Composition) 관계인 WeatherData 인터페이스의 get 메서드를 직접 호출하여 데이터를 받아온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Observer
public interface Observer {
public void update();
}
// WeatherData
public class WeatherData implements Subject {
//...
public void notifyObservers() {
for (Observer observer : observers) {
observer.update()
}
}
//...
}
// CurrentConditionDisplay
public class CurrentConditionDisplay implements Observer, Display {
WeatherData weatherData;
// ...
public void update() {
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
// ...
}
출처 : 헤드 퍼스트 디자인 패턴
This post is licensed under CC BY 4.0 by the author.