Typescript로 다시 쓰는 GoF - Observer

아홉번째태양·2023년 10월 10일
0

Observer 패턴이란?

앞선 Mediator 패턴에서 비교를 했던 Pub/Sub 패턴이 Observer 패턴이다.

Observer패턴은 어떤 객체의 상태를 관찰(Observe)하고 상태가 변화했을 때 통지를 받아 해당 객체의 상태를 관찰하는 객체들 간에 동기화하기 위해 사용하는 디자인 패턴이다. 즉, 엄밀히 얘기하면 관찰보다는 관찰 대상의 상태를 전달 받는 것이 보다 중요한 패턴이다.

구현

Observer 패턴에는 다음의 네 가지 객체가 등장한다.
1. Subject 관찰대상
Observer들이 관찰하는 대상으로 관찰의 목적이 되는 어떠한 상태를 가짐
2. ConcreteSubject 구체적인 관찰대상
관찰하는 대상을 구현
3. Observer 관찰자
Subject로부터 상태가 변화했음을 전달 받는 인터페이스를 정의
4. ConcreteObserver 구체적인 관찰자
Observer의 인터페이스를 구현하며 이를 통해 변화한 Subject의 현재 상태를 얻음

Observer

관찰하는 대상의 상태 변화를 전달 받을 수 있는 메소드 update

interface Observer {
    update: (generator: NumberGenerator) => Promise<void>;
}

Subject

관찰 대상이 되는 객체 NumberGenerator의 행동과 Observer들을 관리하기 위한 인터페이스를 정의

abstract class NumberGenerator {
    private observers = new Set<Observer>();

    addObserver(observer: Observer) {
        this.observers.add(observer);
    }

    deleteObserver(observer: Observer) {
        this.observers.delete(observer);
    }

    notifyObservers() {
        this.observers.forEach((observer) => {
            observer.update(this);
        });
    }

    abstract getNumber(): number;
    abstract execute(): void;
}

ConcreteSubject

NumberGenerator의 인터페이스를 구현하며 관찰 목적이 되는 상태 number를 갖는다.

class RandomNumberGenerator extends NumberGenerator {
    private number = 0;

    getNumber(): number {
        return this.number;
    }

    execute(): void {
        for (let i = 0; i < 10; i++) {
            this.number = Math.floor(Math.random() * 50);
            this.notifyObservers();
        }
    }
}

ConcreteObserver

Observer의 구현체들로 Subject의 상태 변화를 전달 받고 각자가 맡은 역할을 수행한다.

import { setTimeout as sleep } from "timers/promises";

class DigitObserver implements Observer {
    async update(generator: NumberGenerator): Promise<void> {
        console.log(`DigitObserver: ${generator.getNumber()}`);
        await sleep(100);
    }
}

class GraphObserver implements Observer {
    async update(generator: NumberGenerator): Promise<void> {
        console.log(`GraphObserver: ${"*".repeat(generator.getNumber())}`);
        await sleep(100);
    }
}

NumberGenerator의 상태를 전달받기 위해 인스턴스를 메소드 update의 매개변수로 받고 있다. 위처럼 단순한 예제에서는 그냥 생성된 숫자만 update(randomNumber: number)로 받아도 크게 상관은 없으나, 그럴 경우 Observer가 관찰하고 있는 대상이 늘어날 경우 해당 값이 어떤 대상에서 온 값인지 구분하기가 어려워진다. 또한, Observer가 필요로 하는 데이터가 무엇인지를 Subject의 입장에서 신경쓰고 맞춰서 처리해야할 수도 있다.

실행

const generator = new RandomNumberGenerator();
const observer1 = new DigitObserver();
const observer2 = new GraphObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
generator.execute();
DigitObserver: 22
GraphObserver: **********************
DigitObserver: 3
GraphObserver: ***
DigitObserver: 35
GraphObserver: ***********************************
DigitObserver: 11
GraphObserver: ***********
DigitObserver: 28
GraphObserver: ****************************
DigitObserver: 24
GraphObserver: ************************
DigitObserver: 9
GraphObserver: *********
DigitObserver: 26
GraphObserver: **************************
DigitObserver: 28
GraphObserver: ****************************
DigitObserver: 25
GraphObserver: *************************

메소드 execute가 실행되면서 임의의 숫자를 생성하고, 생성된 숫자가 각 Observer들에게 전달되면 성공

참고자료

Java언어로 배우는 디자인 패턴 입문 - 쉽게 배우는 Gof의 23가지 디자인패턴 (영진닷컴)

0개의 댓글