观察者模式(Observer Pattern)

观察者模式是一种对象间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖他的对象都将得到通知。通常运用在对象之间的消息通讯中。

// 比如现在有一群学生,可以组成小组,可以寻求帮助

class Students {
    constructor(name, level) {
        this.name = name;
        this.level = level;
        this.tasks = [];
    }

    askForHelp(subject) {
        console.log(`关于${subject}, ${this.level}${this.name}向大家寻求帮助!`)
        this.tasks.forEach((fn) => {
            fn(subject);
        });
    }

    makeTeam(student, fn) {
        console.log(`${this.level}${this.name}与${student.level}${student.name}组成了学习小组!`);
        student.tasks.push(fn);
    }
}

const studentWango = new Students('Wango', '学神');
const studentLily = new Students('Lily', '学霸');
const studentTom = new Students('Tom', '平平无奇的');
const studentPeter = new Students('Peter', '学渣');

// 老师说Peter的成绩比较差,所以大家都和Peter组成了学习小组
studentWango.makeTeam(studentPeter, function(subject) {
    console.log(`Wango说:${subject}确实很难,我也花了好几分钟复习才堪堪得到了满分!`);
})
// 学神Wango与学渣Peter组成了学习小组!

studentLily.makeTeam(studentPeter, function(subject) {
    console.log(`Lily说:${subject}!? 背就完事了!`);
})
// 学霸Lily与学渣Peter组成了学习小组!

studentTom.makeTeam(studentPeter, function(subject) {
    console.log(`Tom说:${subject}的话,一定要好好学,但学不好也没事,我们都是凡人!`);
})
// 平平无奇的Tom与学渣Peter组成了学习小组!

// 有一天...
studentPeter.askForHelp('英语');
// 关于英语, 学渣Peter向大家寻求帮助!
// Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!
// Lily说:英语!? 背就完事了!
// Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!

发布订阅模式(Pub-Sub Pattern)

发布订阅模并不在24种设计模式之中,只是观察者模式的一种变体。相比观察者模式,订阅发布模式新增了一个任务调度中心,订阅者将自己想要订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是触发该事件时,由调度中心同一调度订阅者注册到调度中心的代码。

// 同样的学习小组

class Students {
    constructor(name, level) {
        this.name = name;
        this.level = level;
    }
    // 订阅事件
    on(type, fn) {
        ClassRoom.subscribe(type, fn);
    }
    // 发布内容
    emit(type, content) {
        ClassRoom.askForHelp(type, content);
    }
}
// 任务调度中心
class ClassRoom {
    static topic = Object.create(null)
    // 对于某个事件发布任务
    static askForHelp(type, content) {
        console.log(`${type}:${content}`);
        if (!ClassRoom.topic[type] || ClassRoom.topic[type].length === 0) {
            console.log(`然而并没有学生对${type}感兴趣!`);
            return;
        }
        ClassRoom.topic[type].forEach((fn) => {
            fn(content);
        });
    }
    // 订阅某个类型的事件
    static subscribe(type, fn) {
        if (!ClassRoom.topic[type]) {
            ClassRoom.topic[type] = [];
        }
        console.log(`有学生订阅了${type}.`);
        ClassRoom.topic[type].push(fn);
    }
}

const studentWango = new Students('Wango', '学神');
const studentLily = new Students('Lily', '学霸');
const studentTom = new Students('Tom', '平平无奇的');
const studentPeter = new Students('Peter', '学渣');

studentWango.on('Life', function(type, content) {
    console.log(`Wango说: 终于有人问生活上的问题了,果然还是做个凡人比较快乐啊!`);
})
// 有学生订阅了Life.

studentWango.on('English', function(type, content) {
    console.log(`Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!`);
})
// 有学生订阅了English.

studentTom.on('English', function(type, content) {
    console.log(`Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!`);
})
// 有学生订阅了English.

studentLily.on('English', function(type, content) {
    console.log(`Lily说: 英语?! 背就完事了!`);
})
// 有学生订阅了English.

studentLily.emit('Life', '这个世界有英雄吗?');
// Life:这个世界有英雄吗?
// Wango说: 终于有人问生活上的问题了,果然还是做个凡人比较快乐啊!

studentTom.emit('English', '单词记不住怎么办啊?');
// English:单词记不住怎么办啊?
// Wango说:英语确实很难,我也花了好几分钟复习才堪堪得到了满分!
// Tom说:英语的话,一定要好好学,但学不好也没事,我们都是凡人!
// Lily说: 英语?! 背就完事了!

studentPeter.emit('Game', '学什么?玩游戏啊!');
// Game:学什么?玩游戏啊!
// 然而并没有学生对Game感兴趣!

区别与联系

相比观察者模,订阅发布模式中的的订阅者和发布者分离更加彻底。订阅者在订阅的时候只关注事件本身,而不用关心是谁发布的信息;而发布者同样只需要关注发布的内容,不用关心是谁在订阅,如何处理。

总的来说,尽管订阅发布模式多了个调度中心,但使用起来可以更加灵活,对多事件的支持更好,当然,内存消耗更大,毕竟要为维护多个事件队列。

03-30 19:59