typescript 类的所有示例上的观察者模式

pn9klfpd  于 2023-01-03  发布在  TypeScript
关注(0)|答案(2)|浏览(125)

我正在尝试学习观察者模式,我想到的一个例子是,我想观察类的任何示例何时更新,而不仅仅是一个单例。我整理的一个例子是,我们有一个DeliverySystem观察者,它想知道任何Burger何时被修改。我们可以尝试用以下代码来实现这一点:

class Burger {
    static observers: Observer[] = [];

    static addObserver(observer: Observer) {
        Burger.observers = Burger.observers.concat(observer);
    }

    id: number;

    constructor(id: number) {
        this.id = id;
    }

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

interface Observer {
    update: (target: any) => void;
}

class DeliverySystem implements Observer {
    update(target: any) {
        console.log("DeliverySystem got pizza " + target.id);
    }

    observe() {
        Burger.addObserver(this);
    }
}

const deliverySystem = new DeliverySystem().observe();
new Burger(12345).cook();

这通常是可行的,但我不确定如何扩展它,使我的DeliverySystem可以观察其他食物。如果我添加一个Hotdog类,BurgerHotdog可以实现什么接口,使我不必分别处理它们的观察者?我希望能够像下面这样编写Burger,但我真的不确定如何编写:

class Burger implements Observable /* <-- what does this look like? */ {
    id: number;

    constructor(id: number) {
        this.id = id;
    }

    cook() {
        updateObservers(this);
    }
}
8e2ybdfx

8e2ybdfx1#

我将为您指出Refactoring Guru
您希望发布者(Observable)看起来像这样:

interface Subscriber {
    update(): void;
}

interface Publisher {
    subscribe(s: Subscriber): void;
    unsubscribe(s: Subscriber): void;
    notifySubscribers(): void;
}

如果愿意,您甚至可以更进一步,使它们通用。

interface Subscriber<C> {
    update(content: C): void;
}

interface Publisher<C> {
    subscribe(s: Subscriber<C>): void;
    unsubscribe(s: Subscriber<C>): void;
    notifySubscribers(): void;
}
oxiaedzo

oxiaedzo2#

如果您希望能够观察所有示例,那么PublishSubscribe模式可能更合适,更具体地说,Domain Events模式更合适。
有两种主要的策略来分派事件,但最简单的是有一个PubSub单例。

//pub-sub singleton for events
const domainEvents = {
    _subscribers: [],
    publish(event) {
        this._subscribers.forEach(s => s(event));
    },
    subscribe(subscriber) {
        this._subscribers.push(subscriber);
    }
};

class BurgerCooked {
    constructor(burgerId) {
        this.burgerId = burgerId;
        Object.freeze(this);
    }
}

class Burger {
    constructor(id) {
       this._id = id;
       this._cooked = false;
    }
    
    get id() { return this._id }
    get cooked() { return this._cooked }
    cook() {
        if (this.cooked) {
           throw new Error('already cooked');
        }
        this._cooked = true;
        domainEvents.publish(new BurgerCooked(this.id));
    }
}

domainEvents.subscribe(e => {
   if (e instanceof BurgerCooked) {
       console.log(`${e.burgerId} is cooked!`);
   }
});

const b1 = new Burger('b1');
const b2 = new Burger('b2');

b1.cook();
b2.cook();

通常域事件不会携带直接的聚合(例如Burger)引用,以便于它们的序列化和存储,并确保它们是不可变的。通常有一个Repository模仿内存中的收集,并允许通过id检索聚合示例。

相关问题