[Behavioral Pattern] 옵저버 패턴 (Observer Pattern)
Design Pattern / Behavioral Pattern
옵저버 패턴의 정의와 해당 디자인 패턴의 예제 코드를 통한 이해 및 설명 정리
개념
어떤 이벤트가 일어나는 것을 감시하는 패턴을 의미
함수로 직접 요청한 적 없지만 시스템에 의해 발생하는 동작들을 이벤트라고 하는데, 이러한 이벤트들을 감시하여 이벤트가 발생할 때마다 미리 정의해 둔 어떠한 동작을 즉각 수행하게 해주는 프로그래밍 패턴
옵저버 패턴을 활용하면 다른 객체의 상태 변화를 별도의 함수 호출 없이 즉각적으로 알 수 있기 때문에, 이벤트에 대한 처리를 자주 해야 하는 프로그램이라면 매우 효율적인 프로그램을 작성할 수 있음
여타 다른 디자인 패턴들과 다르게 일대다(
one-to-many
) 의존성을 가지는데, 주로 분산 이벤틈 핸들링 시스템 구현을 하는데 사용함Pub/Sub
모델로도 알려져 있음
패턴 구조
Subject
- 관찰 대상자를 정의하는 인터페이스
ConcreteSubject
관찰 당하는 대상자 / 발행자 / 게시자
Observer
들을 리스트(List
,Map
,Set
등…)로 모아 합성(composition
)하여 가지고 있음Subject
의 역할은 관찰자인Observer
들을 내부 리스트에 등록/삭제하는 인프라를 갖고 있음 (register
,remove
)Subject
가 상태를 변경하거나 어떤 동작을 실행할 때,Observer
들에게 이벤트 알림(notify
)을 발행
Observer
- 구독자들을 묶는 인터페이스 (다형성)
ConcreteObserver
관찰자 / 구독자 / 알림 수신자
Observer
들은Subject
가 발행한 알림에 대해 현재 상태를 취득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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
interface ISubject {
registerObserver(o: Observer): void; // 구독 추가
removeObserver(o: Observer): void; // 구독 삭제
notifyObservers(): void; // Subject 객체의 상태 변경 시, 이를 모든 옵저버에게 알림
}
class WeatherAPI implements ISubject {
temp!: number; // 기온
humidity!: number; // 습도
pressure!: number; // 기압
// 구독자들을 담아 관리하는 리스트
subscribers: Array<Observer> = new Array();
measurementsChanged(): void {
// 랜덤 값을 통해 날씨 API처럼 구현
this.temp = parseFloat((Math.random() * 100).toFixed(2));
this.humidity = parseFloat((Math.random() * 100).toFixed(2));
this.pressure = parseFloat((Math.random() * 100).toFixed(2));
this.notifyObservers(); // 값이 변화하면 바로 옵저버들에게 발행
}
public registerObserver(o: Observer): void {
this.subscribers.push(o); // 구독자 추가
}
public removeObserver(o: Observer): void {
const idx = this.subscribers.indexOf(o);
if (idx > -1) this.subscribers.splice(idx, 1); // 구독자 삭제
}
// 이벤트 전파
public notifyObservers(): void {
// JAVA -> for(Observer o: subscribers) {
for (let o of this.subscribers) {
o.display(this); // 자신의 객체를 매개변수로 줘서 현재 자신의 상태를 구독자에게 알림
}
}
}
interface Observer {
display(api: WeatherAPI): void;
}
class KoreanUser implements Observer {
name: string;
constructor(name: string) {
this.name = name;
}
public display(api: WeatherAPI): void {
console.log(
`${this.name}님이 현재 날씨 상태를 조회함 : ${api.temp}°C ${api.humidity}g/m3 ${api.pressure}hPa`
);
}
}
class Client {
public static main(_args?: string[]): void {
const api: WeatherAPI = new WeatherAPI();
const user_1 = new KoreanUser("한형진");
const user_2 = new KoreanUser("홍길동");
const user_3 = new KoreanUser("임꺽정");
const user_4 = new KoreanUser("한진형");
api.registerObserver(user_1);
api.registerObserver(user_2);
api.registerObserver(user_3);
api.measurementsChanged();
api.removeObserver(user_3);
api.registerObserver(user_4);
console.log("");
api.measurementsChanged();
}
}
Client.main();
// 한형진님이 현재 날씨 상태를 조회함 : 2.55°C 12.32g/m3 92.05hPa
// 홍길동님이 현재 날씨 상태를 조회함 : 2.55°C 12.32g/m3 92.05hPa
// 임꺽정님이 현재 날씨 상태를 조회함 : 2.55°C 12.32g/m3 92.05hPa
// 한형진님이 현재 날씨 상태를 조회함 : 47.03°C 65.19g/m3 43.94hPa
// 홍길동님이 현재 날씨 상태를 조회함 : 47.03°C 65.19g/m3 43.94hPa
// 한진형님이 현재 날씨 상태를 조회함 : 47.03°C 65.19g/m3 43.94hPa