Design Pattern

Observer pattern, 옵저버 패턴

iKay 2020. 5. 18. 22:16
반응형

Observer Pattern 이란

어떤 객체에서 데이터의 변경이 발생했을 때, 그 객체에 의존하지 않고 데이터의 변경을 통보할 때 사용한다. 그래서 통보 받는 대상은 수평적으로 비교적 쉽게 기능을 확장 될 수 있다.

 

 

Observer Pattern 적용

아래는 스타크래프트의 테란 Factory가 벌쳐, 탱크 그리고 골리앗을 생산완료 했을 때, 인구수가 늘어나는 것을 보여주는 SupplyViewService와 생산이 완료 됐음을 알려주는 Notification Service에 생산이 완료됐음을 통보해 인구수를 업데이트, 생산된 직후 사용자에게 알림을 하는 것을 구현한 것이다.

 

보통 보면 observer를 구현하는 객체가 Subject를 구체화한 클래스의 객체를 가져아 하는 것 같은데 생략하고 notify할 때 필요한 데이터를 넘겨주는 식으로 변형했다.

 

Class Diagram

 

예제 코드

 

abstract class Subject {
    private observers: Observer[] = [];

    attach(observer: Observer) {
        this.observers.push(observer);
    }

    detach(observer: Observer) {
        // TODO: 구현
    }

    notify(unit: Unit) {
        this.observers.forEach((observer) => observer.update(unit));
    }
}

interface Observer {
    update(unit: Unit): void;
}

class Factory extends Subject {
    produce(unitType: UnitType) {
        const unitId = this.generateUnitId();
        const unitSupplySize = this.getUnitSupplySize(unitType);
        const unit = new Unit(unitId, unitType, unitSupplySize);
        this.notify(unit);
    }

    private static unitId = 0;

    private generateUnitId(): number {
        return Factory.unitId++;
    }

    private getUnitSupplySize(unitType: UnitType): number {
        switch (unitType) {
            case 'vulture':
            case 'tank':
            case 'goliath':
                return 2;
        }
    }
}

class SupplyViewService implements Observer {
    unitSupplySize: number;

    constructor() {
        this.unitSupplySize = 0;
    }

    update(unit: Unit) {
        const { supplySize } = unit;
        this.addUnitSupply(supplySize);
    }

    private addUnitSupply(size: number) {
        this.unitSupplySize += size;
        console.log(`현재 인구 수=${this.unitSupplySize}/200`);
    }
}

class NotificationService implements Observer {
    update(unit: Unit) {
        const { unitType } = unit;
        this.notify(unitType);
    }

    private notify(unitType: UnitType) {
        console.log(`unit: ${unitType}가 생산되었다. `);
    }
}

type UnitType = 'vulture' | 'tank' | 'goliath';

class Unit {
    unitId: number;
    unitType: 'vulture' | 'tank' | 'goliath';
    supplySize: number;

    constructor(unitId: number, unitType: UnitType, supplySize: number) {
        this.unitId = unitId;
        this.unitType = unitType;
        this.supplySize = supplySize;
    }
}

function Main() {
    const factory = new Factory();
    const supplyView = new SupplyViewService();
    const notify = new NotificationService();

    // 구독 등록
    factory.attach(supplyView);
    factory.attach(notify);

    factory.produce('vulture'); // 현재 인구 수=2/200, unit: vulture가 생산되었다.
    factory.produce('tank'); // 현재 인구 수=4/200, unit: tank가 생산되었다.
    factory.produce('goliath'); // 현재 인구 수=6/200, unit: goliath가 생산되었다.
}

Main();

 

결론

소스 코드를 보면 알겠지만 Factory에서 Unit이 생산 완료된 직 후 User에게 생산이 완료됐음을 알리거나, 현재 인구 수를 업데이트 하는 등 어떤 일을 하고 싶을 때 Factory 클래스 수정 없이 해결 가능하다. 이는 SOLID 법칙으로 보면 SRP(Single Responsiblity Principle, 단일책임 원칙)과 OCP(Open-Close Principle, 개방-폐쇠 법칙, 어떤 기능을 추가하기 위해 기존 클래스를 수정하지 않아도 된다)를 만족하게 해주는 것 같다.

 

 

반응형

'Design Pattern' 카테고리의 다른 글

State pattern, 스테이트 패턴  (0) 2020.04.28
Command Pattern, 커맨트 패턴  (0) 2020.04.13
Singleton pattern, 싱글톤 패턴  (0) 2020.04.02
Strategy pattern, 스트레이티지 패턴  (0) 2020.04.01