JavaScript设计模式——Observe模式(观察者模式)

通常一个优秀的项目,会使用到很多的设计模式,这些设计模式在我们的解决方案中,会起到十分重要的作用,它的存在,能使项目的结构更加简洁清晰易于理解
所以了解常用的设计模式,会让我们在日常开发更加得心应手,也会让团队沟通变得更加顺畅。
在《设计模式:可复用面向对象软件基础》一书中强调:当我们评估一个面向对象系统的质量时,所使用的方法之一就是判断系统的设计者是否强调了对象之间的公共系统关系

简介

那么在当前这个博文中,我介绍的就是 JavaScript 语言Observer设计模式
初次接触 Observer设计模式,是在 Vue双向数据绑定中。其原理就是使用了 Observer-观察者 对数据进行劫持,然后让数据的读写全部处于监管之中。然后再使用发布-订阅模式Publish-发布者通知到Subscribe-订阅者,对数据进行进行相应操作,比如双向数据绑定中的视图更新

所以,Vue的一个数据的写入,要经过这样一个流程
流程图

所以,实际上,Vue 数据的双向绑定,使用到了2个设计模式:Obsever模式Publish–Subscribe模式。(有一些人认为,观察者模式和订阅观察)

Observer模式(观察者模式)

就像发明一个新的东西一样,新的发明,必定会有其在现实世界启发
就好比雷达的发明,源自于蝙蝠超声波回声定位火箭的发明,源自于水母、墨鱼反冲运动

无独有偶,观察者模式也一样。在现实社会中,经常会出现观察者这个身份。比如书店,他们就是观察者。既然,有了观察者,那一定会有被观察者,他们就像是出版社
书店“监视”各个出版社的新书,若出版社出现了新书(观察者在监视的行为),则书店获得了出版社的信息(被观察者的对象信息)和出版社的发布新书(被观察者的变化),然后书店将其发布的新书拿到,并告诉关注该类书的读者(观察者做出相应操作)

Observer模式的大致流程如上。话不多说,我们着手开始用 JavaScript 实现Observer模式

设计模式中的对象
Observer 观察者

Observer是一个监视被观察者的对象,它需要提供一个接口,对数据发生改变时,做出的行为进行定义。

Subject 被观察者

在出现观察者关心的事件时,要同时通知多个观察者。

抽象模型的代码实现
Observer 观察者
var Observer = function () {};
// 观察者包含一个notify(通知)的回调方法
Observer.prototype.notify = function (info) {
    // 在该方法内做出操作
    console.log(`观察者发现 ${info} 出现更改`);
};
Subject 被观察者
var Subject = function (info) {
    // 注册自己(被观察者)的信息
    this.info = info;
    // 已经注册的观察者的列表
    this.observerList = [];
};
Subject.prototype = {
    // 注册一个监视当前对象的观察者到观察者列表
    register: function (observer) {
        this.observerList.push(observer);
    },
    // 不在关注当前对象的观察者,注销观察者对象
    remove: function (observer) {
        // 获得当前观察者列表的长度
        var listLength = this.observerList.length;
        for (var i = 0; i < listLength; i++) {
            if ((observer = this.observerList[i])) {
                // 从观测者列表删除该观察者
                this.observerList.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    // 通知所有注册的观察者对象
    update: function () {
        var observer;
        // 获得当前观察者列表的长度
        var listLength = this.observerList.length;
        for (var i = 0; i < listLength; i++) {
            observer = this.observerList[i];
            // 调用Observer对象的notify回调方法
            observer.notify(this.info);
        }
    },
};
小结

以上便是Observer模式的代码实现雏形,主要的流程逻辑已经包括在其中了。但是在具体实现的时候,可以做出部分的调整。

比如:

observer.notify(this.info);

这一句代码可以不一定要传输对象自身的所有信息,放在出版社那,可能只需要传入一个新发布的书名。

那么接下来,我们按照之前的出版社-书店-读者的例子,进行一个具体实例的代码实现。


具体实例的代码实现
Observer 观察者(书店) 定义
var BookStore = function () {};
// 书店包含一个notify(通知)的回调方法
BookStore.prototype.notify = function (CompanyName,BookName) {
    // 在该方法内做出操作
    console.log(`读者快来,我这出了新书:${CompanyName} 发版的 ${BookName}`);
};
Subject 被观察者(出版社) 定义
var Company = function (CompanyName) {
    // 出版社名称
    this.Name = CompanyName;
    // 新的书名
    this.NewBookName = "";
    // 已经注册的书店的列表
    this.bookStoreList = [];
};
Company.prototype = {
    // 注册一个监视当前对象的书店到书店列表
    register: function (bookStore) {
        this.bookStoreList.push(bookStore);
    },
    // 不在关注当前对象的书店,注销书店对象
    remove: function (bookStore) {
        // 获得当前书店列表的长度
        var listLength = this.bookStoreList.length;
        for (var i = 0; i < listLength; i++) {
            if ((bookStore == this.observerList[i])) {
                // 从书店列表删除该观察者
                this.bookStoreList.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    // 通知所有注册的书店对象
    publish: function (BookName) {
        // 记录新发布书的名字
        this.NewBookName = BookName;
        var bookStore;
        // 获得当前书店列表的长度
        var listLength = this.bookStoreList.length;
        for (var i = 0; i < listLength; i++) {
            bookStore = this.bookStoreList[i];
            // 调用BookStore对象的notify回调方法
            bookStore.notify(this.Name,this.NewBookName);
        }
    },
};
具体使用
// 初始化一个出版社——VicoHu出版社
var company = new Company("VicoHu出版社");

// 初始化三家商店
var bookStore1 = new BookStore("小胡书店");
var bookStore2 = new BookStore("二刘书店");
var bookStore3 = new BookStore("大黄书店");


// 重写bookStore3的notify方法实现发布新书时的不同操作
bookStore3.notify = function (CompanyName, BookName) {
    // 在该方法内做出操作
    console.log(
        `${this.Name}促销,新书上架:${CompanyName} 发版的 ${BookName},比其他的店都要便宜很多。`
    );
};

// 将三家商店注册到出版社的商店列表里
company.register(bookStore1);
company.register(bookStore2);
company.register(bookStore3);

// 发布新书
company.publish("《挪威的森林》");

// 控制台输出如下内容
/**
 * 小胡书店有促销活动!!!!! 读者快来,我这出了新书:VicoHu出版社 发版的 《挪威的森林》
 * 二刘书店有促销活动!!!!! 读者快来,我这出了新书:VicoHu出版社 发版的 《挪威的森林》
 * 大黄书店促销,新书上架:VicoHu出版社 发版的 《挪威的森林》,比其他的店都要便宜很多。
 */

小结

在上面的具体实例的代码实现中,我们完成了书店的创建,和出版社的创建,并且将多个商店注册到出版社,并在出版社发布新书的时候,通知给所有的商店,让商店发出广告和通知给我们(读者)。
其实,从本质上而言,商店(观察者)是被动的,它需要依赖于出版社(被观察者调用notify方法,来完成商店的广告发布。
但是,商店的广告的发布,可以通过重写 notify 方法来进行自定义的操作。


总结

Observer 设计模式优点缺点都比较明显

Observer模式 优点
  1. 解耦。这是现在大多数的设计模式都在做的一件事情,例如:MVC、MVVM等。其好处就在于,不需要在设计观察者的时候,关注被观察者的设计。这样可以只需要关注一个notify方法参数。这样的好处很明显,可以把更多的注意力放在设计某个对象的上面。
  2. 广播通讯。如我们后面发布的 《挪威的森林》一书,只需要调用一出版社的publish方法,就可以对所有已经在出版社注册的商店进行通知。
Observer模式 缺点
  1. 观察者过于被动。观察者在这个设计中,其实更像是个被通知的身份,它没有主动了解被观察者的能力。只能根据观察者给的信息,做出反应和操作。
  2. 抗风险性不高。假如某个注册的商店的方法出现问题,或者一直未执行完,会影响到后面的商店的通知。从而影响整体广播的执行。

最后的总结

这世界上没有绝对通用的设计模式,都是根据任务的需求,选择合适的设计模式。
但是,倘若你连某个合适的设计模式都不知道,就会错过一些事半功倍的机会。
所以,只有不断的去学习,不断走出舒适圈,才能让自己走的更远,并成为一个编码更加高效、稳定的开发者。加油吧!各位!

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值