Skip to main content

Observer Pattern

View all Design Patterns

객체 간 1:N 관계를 맺어 하나의 객체(1)에 변경이 생겼을 때, 이에 의존하는 객체들(N)이 알림을 받고 자동으로 갱신되는 방법이다. 주로 분산 이벤트 처리 시스템에서 이벤트를 생성하는 컴포넌트와 이러한 이벤트에 반응하는 컴포넌트 사이의 느슨한 결합을 제공하기 위해 사용된다.


Class Diagram

Abstract overview

Concrete example


Code

interface Subject {
subscribe(observer: Observer): void;
unsubscribe(observer: Observer): void;
notifyObservers(): void;
getState(): number;
setState(state: number): void;
}

interface Observer {
update(subject: Subject): void;
}

class ConcreteSubject implements Subject {
private state: number = 0;
private observers: Observer[];

constructor() {
this.observers = [];
}

getState(): number {
return this.state;
}

setState(state: number): void {
this.state = state;
this.notifyObservers();
}

subscribe(observer: Observer): void {
const isExist = this.observers.includes(observer);
if (isExist) {
console.log("Observer has been already attached.");
return;
}
this.observers.push(observer);
}

unsubscribe(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
if (observerIndex === -1) {
console.log("Observer not found.");
return;
}
this.observers.splice(observerIndex, 1);
}

notifyObservers(): void {
for (const observer of this.observers) {
observer.update(this);
}
}
}

class ConcreteObserver implements Observer {
/** 상태 갱신 */
update(subject: Subject): void {
console.log(
"ConcreteObserver: Event triggered. State:",
subject.getState()
);
}
}

const concreteSubject = new ConcreteSubject();

const observerA = new ConcreteObserver();
const observerB = new ConcreteObserver();

concreteSubject.subscribe(observerA);
concreteSubject.subscribe(observerB);

concreteSubject.setState(10);
// ConcreteObserver: Event triggered. State: 10
// ConcreteObserver: Event triggered. State: 10

setState 함수로 상태를 변화하게 되면, this.notifyObservers() 코드가 실행되면서 로그가 출력되게 된다.


Pros and Cons

Pros

  • SubjectObserver 간의 느슨한 결합으로, 서로 독립적으로 존재하며, 따라서 서로 간의 의존성이 낮다.
  • 새로운 옵저버를 추가하는 것이 간단하다.
  • 런타임에 동적으로 옵저버를 추가하거나 제거할 수 있다.

Cons

  • 옵저버가 올바르게 해제되지 않으면, 가비지 컬렉터가 메모리를 회수하지 못해 메모리 릭(Leak)이 발생할 수 있다.
  • 멀티 스레드 환경에서 동기화 문제(경쟁 상태 또는 데드락)가 발생할 수 있다.
    • 두 개 이상의 스레드가 동시에 Subject의 상태를 변경하거나, Observers 목록을 수정할 때, 데이터가 일관되지 않은 상태가 될 수 있다.
Related Links