A way of notifying change to a number of classes to ensure consistency between the classes.

    定义对象之间一种一对多的依赖关系,当一个对象发生改变时,所有依赖它的对象都将会得到通知并自动更新。


    动机:不希望对象之间「紧耦合」而导致复用性低。

    适用性:当期望两个对象有依赖,却能够独立改变和复用;当一个对象需要修改其它对象,而又不清楚具体的需要修改的对象(数量、接口和通信细节);不希望对象是紧密耦合的。


    一个 Subject 维持一系列依赖于它的 Observers 的对象,将其有关状态的任何改变自动通知给它们。

    • Subject: 维护一系列观察者,方便添加 / 删除观察者
    • Observer:为那些在目标状态发生改变时需获得的通知的对象提供一个更新接口
    • ConcreteSubject: 状态发生改变时,向 Observer 发出通知,存储 ConcreteObserver 的状态
    • ConcreteObserver: 存储一个指向 ConcreteSubject 的引用,实现 Observer 的更新接口,以使自身的状态与目标状态保持一致。

    注意:Observer 模式要求希望接收到主题通知的观察者(或对象)必须订阅内容改变事件。而Pub/Sub 使用了一个主题/事件通道,这个通道介于希望接收到通知(订阅者)的对象和激活事件的对象(发布者)之前


    结构和代码实现:

    1. class Subject {
    2. private observerList: Observer[];
    3. public notify(data) {
    4. this.observerList.forEach(o => o.notify(data));
    5. };
    6. public listen(observer: Observer) {
    7. this.observerList.push(observer);
    8. }
    9. public removeObserver(observer) {}
    10. }
    11. class Observer {
    12. private callback: Function;
    13. private subject: Subject;
    14. constructor(subject, handler) {
    15. this.callback = handler;
    16. this.subject = subject;
    17. this.subject.listen(this);
    18. }
    19. notify() {
    20. this.callback.call(this);
    21. }
    22. }
    23. class DataModel {
    24. private subject: Subject;
    25. onDataChange(data) {
    26. this.subject.notify(data);
    27. }
    28. }
    29. class Chart {
    30. private subject: Subject;
    31. private observer: Observer;
    32. private setState(data) {};
    33. constructor(appData) {
    34. this.observer = new Observer(this.subject, (data) => this.setState(data));
    35. }
    36. }
    37. class Table {
    38. private subject: Subject;
    39. private observer: Observer;
    40. private setState(data) { };
    41. constructor(appData) {
    42. this.observer = new Observer(this.subject, (data) => this.setState(data));
    43. }
    44. }

    实现反思:

    每一个对象即可以具备 Observer 的能力,也可以只具备 Subject 的能力,还可以两者皆具,也就成了 Pub/Sub 模式。


    Pub / Sub

    • 使用了一个主题/事件通道,这个通道介于希望接收到通知的对象和激活事件的对象之间。该事件系统允许代码定义应用程序的特定事件,这些事件可以传递自定义参数,自定义参数即包括订阅者所需要的值。
      • jQuery.on 事件系统 / EventEmitter 为范例
      • Flux / Reflux / Redux 的事件体系也是基于此的
    • 作用:解除对象之间的「直接耦合」,因为消息机制的耦合程度会比对象直接耦合的耦合度低。
    1. var pubsub = {};
    2. (function(q) {
    3. var topics = {},
    4. subUid = -1;
    5. q.publish = function(topic, args) {
    6. if (!topics[topic]) { return false; }
    7. var subscribers = topics[topic],
    8. len = subscribers ? subscribers.length : 0;
    9. while (len -= 1) { subscribers[len].func(topic, args); }
    10. }
    11. q.subscribe = function(topic, func) {
    12. if (!topics[topic]) { topics[topic] = []; }
    13. var token = (++subUid).toString();
    14. topics.topic.push({
    15. token: token,
    16. func: func
    17. });
    18. return token;
    19. }
    20. q.unsubscribe = function(token) {}
    21. })(pubsub);
    22. // ------ DEMO -------
    23. var newCons = function() {};
    24. _.extend(newCons.prototype, pubsub);
    25. var obj = new newCons();
    26. obj.subscribe('topic', func);
    27. obj.publish('topic', data);

    效果:允许独立修改「观察者」和「目标对象」,进而进行单独复用;可以在目标对象没有任何改变的情况下增加其他新的观察者。