Observer 패턴이란?
Observer 패턴이란 어떠한 주체의 상태가 변화하게 되면 이 상태 변화를 감지해서 옵저버들에게 상태 변화를 알려주는 패턴이다.
Observer 패턴은 pub, sub 구조를 통해 표현할 수 있다.
아래 그림은 Observer 패턴을 구현하는 다양한 방식 중 하나이다.
- 주체 (subject) : 상태 변화를 알리려고 하는 객체
- 관찰자 (observer) : 상태가 변경되었을 때 어떠한 처리를 하려고 하는 객체
Observer 패턴을 구현할 때 주체와 객체는 동일할 수도 있고 서로 분리될 수도 있다.
Observer 패턴의 장점
Observer 패턴은 모듈 사이의 결합도를 떨어뜨릴 수 있는 장점이 있다.
observer들은 단순히 주체를 구독하기만 하면 주체는 객체의 상태가 변경될 때마다 알림을 받을 수 있다.
observer들은 주체를 항상 관찰하지 않고 변경이 일어날 때만 알림을 받는 것이다.
이로써 subject와 observer는 서로 분리되어 subject는 상태 변경만 신경 쓰고 observer는 상태 변경 이후 동작에만 신경쓸 수 있다.
또한 새로운 observer를 쉽게 추가할 수도 있다.
Observer 패턴의 활용
Observer 패턴은 다양한 곳에서 활용할 수 있다.
- 이벤트 기반 프로그래밍 : 다양한 관심사가 하나에 몰려 있다면 이벤트 기반 프로그래밍을 통해 관심사를 나눌 수 있다.
- MVC 패턴 : model을 subject, view를 observer로 보면 model의 상태 변경이 발생할 때마다 view를 업데이트 할 수 있다.
- pub - sub 구조 : 구독, 발행 구조에서도 Observer 패턴을 활용할 수 있다.
Observer 패턴 구현하기
자바스크립트로 간단하게 Observer 패턴을 구현해보자.
먼저 subject를 생성해보자.
subject는 다음과 같은 정보를 가지고 있다.
- 자신을 구독 중인 observer 리스트
- 구독, 구독 해제 메소드
- 상태 변화를 observer들에게 알리는 메소드
// 상태 변화를 알릴 주체
class Subject {
#observers;
constructor() {
this.#observers = [];
}
subscribe(observer) {
this.#observers.push(observer);
}
unsubscribe(observer) {
this.#observers.filter((originObserver) => originObserver !== observer);
}
notify(data) {
this.#observers.forEach((observer) => observer.update(data));
}
}
다음은 Observer이다.
// 상태 변화가 일어나면 특정 동작을 수행할 관찰자
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name}에서 변경 사항 수신 : ${data}`);
}
}
주체를 확장하는 상태를 가진 객체도 생성하자.
// 상태를 가지고 있는 객체
class Person extends Subject {
#age;
constructor() {
super();
this.#age = 12;
}
updateAge(newAge) {
this.#age = newAge;
this.notify(newAge);
}
}
내부에 나이 정보를 가지고 있는 객체이다.
<전체 코드>
// 상태 변화를 알릴 주체
class Subject {
#observers;
constructor() {
this.#observers = [];
}
subscribe(observer) {
this.#observers.push(observer);
}
unsubscribe(observer) {
this.#observers.filter((originObserver) => originObserver !== observer);
}
notify(data) {
this.#observers.forEach((observer) => observer.update(data));
}
}
// 상태를 가지고 있는 객체
class Person extends Subject {
#age;
constructor() {
super();
this.#age = 12;
}
updateAge(newAge) {
this.#age = newAge;
this.notify(newAge);
}
}
// 상태 변화가 일어나면 특정 동작을 수행할 관찰자
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name}에서 변경 사항 수신 : ${data}`);
}
}
// 주체 생성
const person = new Person();
// 4개의 관찰자 생성 및 등록
const observer1 = new Observer('observer 1');
const observer2 = new Observer('observer 2');
const observer3 = new Observer('observer 3');
const observer4 = new Observer('observer 4');
person.subscribe(observer1);
person.subscribe(observer2);
person.subscribe(observer3);
person.subscribe(observer4);
person.updateAge(13);
위 코드를 실행해보자.
<실행 결과>
observer 1에서 변경 사항 수신 : 13
observer 2에서 변경 사항 수신 : 13
observer 3에서 변경 사항 수신 : 13
observer 4에서 변경 사항 수신 : 13
Proxy 패턴을 적용하여 Observer 패턴 구현
위에서는 간단하게 클래스 형식으로 Observer 패턴을 구현해보았는데 이번에는 함수 형식으로 Proxy 패턴을 적용하여 Observer 패턴을 구현해보자.
자바스크립트에서는 Proxy 객체를 제공하는데 Proxy 객체는 어떠한 대상에 대한 동작을 가로채서 대신 수행할 수 있도록 해준다.
Proxy 객체를 생성할 때는 두 개의 매개변수가 필요하다.
- target : 동작을 가로챌 대상
- handler : 동작을 가로채서 새로운 동작을 정의할 수 있다.
new Proxy(target, handler);
상태 변경을 감지할 것이기 때문에 여러가지 동작 중 "set" 동작을 가로챌 것이다.
전역적인 상태 저장소(subject)를 만들고 이 상태 변화를 감지하는 View(observer)를 만들어보자.
<store.js>
// 자신을 구독한 observer들
const observers = new Set();
// observer 추가
const addObservers = (observer) => {
observers.add(observer);
};
// 상태
// set 동작이 발생할 경우 대신 target에 접근한 후 observer들에게 알린다.
const state = new Proxy(
{
name: 'chulsoo',
},
{
set: (target, props, value) => {
target[props] = value;
notify(value);
return true;
},
},
);
// observer들에게 변경사항 알리는 함수
const notify = (data) => {
observers.forEach((observer) => observer(data));
};
// 상태 변경하는 함수
const changeName = (name) => {
state.name = name;
};
module.exports = {
addObservers,
state,
changeName,
};
<main.js>
const { addObservers, changeName } = require('./store.js');
const observer1 = (value) => {
console.log(`observer 1 상태 변화 감지 : ${value}`);
};
const observer2 = (value) => {
console.log(`observer 2 상태 변화 감지 : ${value}`);
};
const observer3 = (value) => {
console.log(`observer 3 상태 변화 감지 : ${value}`);
};
const observer4 = (value) => {
console.log(`observer 4 상태 변화 감지 : ${value}`);
};
addObservers(observer1);
addObservers(observer2);
addObservers(observer3);
addObservers(observer4);
changeName("minsoo");
<main.js 실행 결과>
observer 1 상태 변화 감지 : minsoo
observer 2 상태 변화 감지 : minsoo
observer 3 상태 변화 감지 : minsoo
observer 4 상태 변화 감지 : minsoo
댓글