JavaScript设计模式之观察者模式(学习笔记)

设计模式(Design Pattern)对于软件开发来说其重要性不言而喻,代码可复用、可维护、可扩展一直都是软件工程中的追求!对于我一个学javascript的人来说,理解设计模式似乎有些困难,对仅切图、做少量交互效果的FE甚至可能不会用到,但是当你开始使用Angular/Backbone等框架的时候,就无法避免设计模式、MVC/MVVM这些东西了(反正我是伤脑筋)。

我学设计模式是刚开始接触编程大概三个月的时候,看一本书《大话设计模式》,里面用C#语言来写,我很无语,因为强类型的编程语言对于我这种写弱类型的毛头小子来说,似乎又有困难啊,于是我就学C#基础语法规则去了。。。今年年初我又学了JAVA的基础语法规则。。。然而我的初衷已经被抛弃在一旁,落上了厚厚的灰层。对于自学编程的我来说,不知道学习编程的先后顺序似乎吃亏不少,但是总要有开头的!

以上可直接跳过



先来说一下我对“观察者模式”的个人理解:观察者模式又称“发布-订阅(Publish/Subscribe)模式”,发布与订阅显然是两个不同对象的功能,比如RSS。知乎是一个发布者(发布一些对某方面问题的高赞同解答),我作为一个订阅者(在我的邮箱里面订阅了知乎的相关发布内容),我的同事以及我的老板都订阅了知乎,所以在这个模型中,有一个发布者,有三个订阅者。

在具体编程中,发布者有了新的内容,需要向订阅者推送数据,那么新的内容(state)、订阅者有哪些(observers)就是发布者需要包含的东西,谁订阅了、谁退订了则要对发布者中的订阅者列表进行更新。以下是发布者的相关信息代码解读:

//发布者
        function Publisher(){
            this.observers = [];
            this.state = "";

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.state);
            };
        }

以上在遍历observers数组的时候,可以使用数组类的filter、forEach等新特性来处理。第三个notice函数表示发布者有了新东西,然后对订阅者列表中的所有人通知他们我有新内容(state)了,你们拿去更新你们的邮箱吧。这里把内容传递给了每一个订阅者的update更新功能。

那么订阅者呢?订阅者很简单,只需要具有一个update功能即可(每一个订阅者update可能不一样,比如我是放进邮箱了,我的同事则将订阅的拿来,并且顺便把旧的删掉了,我的上司则将数据转发到Gmail去了)。下面是订阅者相关信息代码解读:

//订阅者
        function Subscribe(){
            this.update = function(data){
                  console.log(data);
            };
        }

实际上,因为每一个订阅者都有这个update,所以我们通常应该将其添加到构造器的原型上面,当对这个默认的update功能不满足要求的时候,可以为每一个订阅者的实例设置单独的update,比如将这个data发送给别人。最后咱们看看怎么应用。

//实际应用
        var oba = new Subscribe(),
            obb = new Subscribe();

        var pba = new Publisher();

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            console.log(state+"hello!");
        }
        obb.update = function(state){
            console.log(state+"world!");
        }
        pba.state = "open ";
        pba.notice();

大家看到,我们在最后对发布者手动设置了它的内容(state)并且要求他发出通知(notice)。在实际项目中,发布者的内容可能是从后台获取的也可能是从前台某地方输入的。然而发布者每次更新内容后又要手动调用通知是不是有点多余呢?既然更新了内容那就肯定要通知别人了啊。那我们就把内容的更新与发出通知进行绑定好了,看下面的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        //发布者
        function Publisher(){
            this.observers = [];
            var state = "";     //让该内容不能直接访问

            //新增两个对于state的操作 获取/更新
            this.getState=function(){
                return state;
            }
            this.setState=function(value){
                state = value;
                this.notice();
            }

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.getState()); //获取发布者的内容
            };
        }

        //订阅者
        function Subscribe(){
            this.update = function(data){
                  console.log(data);
            };
        }

        //实际应用
        var oba = new Subscribe(),
            obb = new Subscribe();

        var pba = new Publisher();

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            console.log(state+"hello!");
        }
        obb.update = function(state){
            console.log(state+"world!");
        }
        pba.setState("open "); //发布者更新了内容
    </script>
</body>
</html>

对于以上的内容,或许并没有跟我们的项目中实际出现的问题有关,那我们就来代入这种设计模式,做一个例子:三个文本框ABC,其中A可编辑,B与C不可编辑且B的值是A的值加上后缀"@w3c.com",C的值是A的值加上前缀"ID-"。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        <label>用户名称:<input type="text" id="pba" placeholder="请输入用户名称" /></label><br /><br />
        <label>生成邮箱:<input type="text" id="oba" readonly /></label>
        <label>生成ID:<input type="text" id="obb" readonly /></label>
    </div>

    <script type="text/javascript">
        //发布者
        function Publisher(obj){
            this.observers = [];
            var state = obj.value;     //让该内容不能直接访问

            //新增两个对于state的操作 获取/更新
            this.getState=function(){
                return state;
            }
            this.setState=function(value){
                state = value;
                this.notice();
            }
            this.obj = obj;

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.getState());
            };
        }

        //订阅者
        function Subscribe(obj){
            this.obj = obj;
            this.update = function(data){
                this.obj.value = data;
            };
        }

        //实际应用
        var oba = new Subscribe(document.querySelector("#oba")),
            obb = new Subscribe(document.querySelector("#obb"));

        var pba = new Publisher(document.querySelector("#pba"));

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            this.obj.value = state+"@w3c.com";
        }
        obb.update = function(state){
            this.obj.value = "ID-"+state;
        }

        pba.obj.addEventListener(‘keyup‘,function(){
            pba.setState(this.value);
        });

    </script>
</body>
</html>

在《大话设计模式》一书中,提到类似的情况:如果针对发布者内容而订阅者要做不同的事情呢?比如一个按钮和三个矩形,点击按钮的时候,第一个矩形增加宽度,第二个矩形增加高度,第三个矩形则变成圆角矩形又该怎么做呢?当然我们可以在三个矩形的update内部写具体的实现代码,但是这update岂不是没有一个具体的功能描述了吗?该书中用“事件委托”解决了这个问题(此处事件委托和DOM中的事件委托应该是两码事),我个人理解这个“事件委托”在javascript中可以用一个数组表示,然后里面放各个订阅者的不同名字的update,然后一一调用。

在《javascript设计模式》一书中,关于观察者模式的实现也是采用”推“这种方式,章节的最后反问到如何实现”拉“这种方式呢?

我个人理解:发布者推送数据的时候有强制性,促使订阅者更新(update),然而在”拉“这种模式中,发布者本身仅仅包含最新的内容,没有通知(notice)没有订阅者列表,当订阅者需要得到数据的时候在其对应的update方法里面传入发布者对象即可。小白之见,请对该模式有不同理解的道友多多指正。o(∩_∩)o

时间: 2024-10-08 10:33:10

JavaScript设计模式之观察者模式(学习笔记)的相关文章

[GeekBand] 设计模式之观察者模式学习笔记

本文参考文献::GeekBand课堂内容,授课老师:李建忠 :网络资料: http://blog.csdn.net/hguisu/article/details/7556625 本文仅作为自己的学习笔记,不代表权威,因为是初学,有错误烦请大家指正,谢谢. 1.什么是观察者模式,应用场所有哪些? 观察者模式(有时又被称为发布-订阅Subscribe>模式.模型-视图View>模式.源-收听者Listener>模式或从属者模式)是软件设计模式的一种.在此种模式中,一个目标物件管理所有相依于它

《Javascript 设计模式》-读书笔记

第七章   设计模式类别 一.设计模式有三种类别 1.创建型模式:专注于处理对象机制.包括:构造器.工厂.抽象.原型.单例.生成器 2.结构型模式:用于找出在不同对象之间建立关系密切的简单方法. 包括:装饰者.外观.享元.适配器.代理 3.行为模式:用于改善或简化系统中不同对象之间的通信.包括:迭代器.中介者.观察者.访问者 第八章   设计模式分类 <Javascript 设计模式>-读书笔记,布布扣,bubuko.com

《Javascript权威指南》学习笔记之十一:处理字符串---String类和正则表达式

一.正则表达式的基本语法 1.概念:正则表达式由普通字符和特殊字符(元字符)组成的文本模式,该模式描述在查找字符串主体时待匹配的一个或者多个字符串.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 普通字符包括所有的大小写字母字符.所有数字.所有标点符号及一些特殊符号.普通字符本身可以组成一个正则表达式,也可以和元字符组合组成一个正则表达式:而元字符则具有特殊的含义,包括().[].{}./.^.$.*.+.?...|.-.?:.?=.?! 2.基本语法 3.优先权含义 二.使用

&lt;&lt;Head First设计模式&gt;&gt;之观察者模式学习

1.概念 定义了对象之间的一对多依赖关系,当一个对象(主题对象)的状态改变时,它的所有依赖者(观察者对象)都会收到通知并自动更新. 2.优势 观察者模式实现了主题对象与观察者对象之间的松耦合,当有新的观察者时,无需修改主题对象的代码,只需要新的观察者对象实现接口.在程序运行的过程中,可以随时注册和删除观察者而不影响主体对象. 观察者模式使主题(Subject)对象和观察者(Observer)对象都可以被轻易地复用. 3.Java中观察者模式 Java API有内置的观察者模式,java.util

《JavaScript权威指南》学习笔记之二十---XMLHttpRequest和AJAX解决方案

一.AJAX概述 AJAX是Asynchronous JavaScript and XML的缩写,中文译作异步JavaScript和XML.AJAX 不是新的编程语言,而是一种使用现有标准的新方法,在不重新加载页面的情况下,与服务器交换数据并更新部分网页的艺术.其核心是:客户端的Javascript能够与web服务器进行异步数据交换. 二.AJAX基础---XMLHttpRequest对象 所有现代浏览器(IE7+.Firefox.Chrome.Safari 以及 Opera)均内建 XMLHt

《Javascript权威指南》学习笔记之十六:BOM之源---BOM基本应用

BOM的基本应用包括:管理浏览器历史.解析地址和获取浏览器信息,本文将介绍这些应用. 一.浏览历史管理 1.history对象的方法和属性 History 对象包含用户(在浏览器窗口中)访问过的 URL,是 window 对象的一部分,可通过 window.history 属性对其进行访问.没有应用于 History 对象的公开标准,不过所有浏览器都支持该对象. length属性:返回浏览器历史列表中的URl数量.是"前进"和"后退"两个按钮之下包含的地址数的总和.

《Javascript权威指南》学习笔记之十九--HTML5 DOM新标准---处理文档元信息和管理交互能力

一.了解DOM 1.DOM是Document Object Model的缩写,即文档对象类型,是文档在内存中的表示形式,是一个应用程序接口,定义了文档的逻辑结构以及一套访问和处理文档的方法. 2.HTML DOM与Core DOM的区别:前者提供了大量的方法和属性,与现有的程序模型一致,更便于脚本的编写者控制. 二.document对象 使用window.document属性返回一个document对象,代表当前window内加载的文档.window可以省略.winName.document返回

设计模式--单例模式(学习笔记)

定义:      单例模式:保证一个类只有一个实例,并且提供一个访问它的全局访问点.(<大话设计模式定义>): 单例模式分为两种: 1>饿汉式单例类:自己被加载时就将自己实例化. 例子:       private SingleExample() {           } private static SingleExample singleExample=new SingleExample(); @Override protected SingleExample getInstanc

《Javascript权威指南》学习笔记之十八:BOM新成就(1)--客户端存储数据(Web SQL DataBase实现)

使用本地存储和会话存储可以实现简单的对象持久化,可以对简单的键值对或对象进行存储.但是,对于比较复杂的关系数据进行处理时,就要用Web SQL Database.浏览器对Web SQL Database的支持情况如图: 一.如何使用Web SQL Database <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-t

《Javascript权威指南》学习笔记之十二:数组、多维数组和符合数组(哈希映射)

Array(数组)是JavaScript中较为复杂的数据类型,同Java.C#.VB等程序语言的数组相比,Javascript数组中的元素不必为相同的数据类型,可以在数组每个元素上混合使用数字.日期.字符串.Object,甚至添加一个嵌套数组. 一.创建数组 1.var arr1 = new Array(); /var  arr2 = new Array(length); /var arr3 = new Array(element1,element2...); var arr4 = [eleme