JavaScript设计模式 (一、观察者模式,发布订阅模式)

我们要了解设计模式之前,先要了解一下设计原则。
大概分为五大原则(SOLID):

  1. 单一职责原则(Single Responsibility Principle)一个程序就做好一件事,尽量纯一些
  2. 开放封闭原则(Open Close Principle) 对扩展开放,对修改封闭
  3. 里氏替换原则(Liskov Substitution Principle)子类能覆盖父类,所有父类能出现的地方,子类都能出现
  4. 接口隔离原则(InterfaceSegregation Principles)保持接口独立单一,一个接口不要包揽所有事
  5. 依赖倒置原则(Dependence Inversion Principle)面向接口编程,依赖于抽象而不依赖于具体

所谓的设计模式,也是经过岁月打磨之后。总结出来的遵循这五大设计原则的,一套行之有效的可以复制的解决某些问题的模板套路。如果在程序设计和程序编写的时候,咱能够时刻注意遵循这五大原则,甚至不用学习23种具体的设计模式套路。说不定咱也能写出来优质代码,只是不知道这是一种模式罢了。
今天先讲观察者模式/发布订阅模式,为什么第一篇是这个模式呢?
因为对于前端来说,这是极其重要的。换句话说,如果只会一种设计模式,那应该就是观察者模式了。
有的资料上会区分观察者模式和发布订阅模式,尤其是面试的时候。感觉没有必要。记那么多干啥,是吧。都是一对一或者一对多的模式。就是说:一个程序,通知其他程序(一个或者多个)去干活。就这么个事。只是实现方式上有稍微的区别而已,根据具体的业务场景去调节就可以了。
观察者模式是有一个主题,平常可能有自己的任务,同时保存了状态和一个观察者列表,如果某个状态发生变化的时候就通知所有观察者去干活。

// 主题 保存状态,和保存所有的观察者 状态变化之后 通知 所有的观察者
class Subject {
    constructor() {
        this.state = 0;
        this.observers = [];
    }
    setState(val) {
        this.state = val;
        this.notifyAllObaservers(val); // 状态改变 通知所有的观察者,也可以将新值传进去,根据新值操作
    }
    notifyAllObaservers(val) { // 状态改变 通知所有的观察者
        // 所有的观察者执行自己的update方法,就是干自己的活了
        this.observers.forEach(observer => observer.update(val)); 
    }
    addObserver(observer) { // 添加新的观察者
        this.observers.push(observer);
    }
}

// 观察者 
class Observer {
    constructor(name, subject) {
        // 在new观察者的时候 就要将该观察者 放入到观察者列表中去
        this.name = name;
        subject.addObserver(this); // 将该观察者 放入到观察者列表中去
    }
    update(val) {
        console.log(`${this.name} 更新了, 最新状态值为: ${val}`)
    }
}

// 测试
const subject = new Subject(); // 创建主题
const observer1 = new Observer('observer1', subject); // 创建观察者
const observer2 = new Observer('observer2', subject);
const observer3 = new Observer('observer3', subject);
subject.setState(1);
subject.setState(2);
subject.setState(3);

发布订阅模式,只不过是多了一个调度中心。主题不再直接通知观察者去干活了。而是主题通知调度中心,由调度中心去通知观察者去干活。这种耦合度更低。适合主题不能直接跟观察者通信的时候,或者不适合直接通信的时候。订阅者可以自定义订阅哪些消息。Vue中的时间总线就是发布订阅者模式。本质还是一个程序,通知其他程序(一个或者多个)去干活。

// 调度中心 维护 订阅消息和订阅者列表
class DispatchCenter {
    constructor() {
        this.subjects = {} // 初始 保存订阅消息和该订阅消息下的函数集合
    }

    // 订阅 消息
    on(subscriberMsg, fn) {
        if (!this.subjects[subscriberMsg]) {
            this.subjects[subscriberMsg] = [];
        }
        this.subjects[subscriberMsg].push(fn);
    };

    // 解除订阅
    off(subscriberMsg, fn) {
        if (!fn) { // 如果不传 就全部解除 该消息下的所有绑定事件
            this.subjects[subscriberMsg] = [];
        } else {
            const index = this.subjects[subscriberMsg].indexOf(fn);
            index >= 0 && this.subjects[subscriberMsg].splice(index, 1);
        }
    };

    // 发布者 发布消息
    emit(subscriberMsg, ...data) {
        this.subjects[subscriberMsg].forEach(item => item(...data));
    };
}

// 发布者
class Publisher {
    constructor(subscriber, data) {
        this.subscriber = subscriber;
        this.data = data;
    }
}

// 订阅者
class Subscriber {
    constructor(subscriber, fn) {
        this.subscriber = subscriber;
        this.fn = fn;
    }
}

const subscriberOne = new Subscriber(
    "1号订阅信息",
    (...args) => {
        console.log("subscriberOne的回调", ...args);
    }
);

const subscriberTwo = new Subscriber(
    "测试发布信息",
    (...args) => {
        console.log("subscriberTwo的回调", ...args);
    }
);

const publs = new Publisher(
    "测试发布信息",
    "携带参数1"
);

const eventBus = new DispatchCenter(); // eventBus 熟不熟悉 这个名字

// 订阅者1,2号订阅各自消息
eventBus.on(subscriberOne.subscriber, subscriberOne.fn);
eventBus.on(subscriberTwo.subscriber, subscriberTwo.fn);

// 发布者 发布 "测试发布信息"相关的事件
// eventBus.off ("测试发布信息", subscriberTwo.fn);
eventBus.emit(publs.subscriber, publs.data, "其他参数");
eventBus.emit("1号订阅信息", publs.data, "另外的参数");

你可能感兴趣的:(JavaScript设计模式 (一、观察者模式,发布订阅模式))