Javascript设计模式:观察者模式

2016-02-19 16:35 12 1 收藏

清醒时做事,糊涂时读书,大怒时睡觉,无聊时关注图老师为大家准备的精彩内容。下面为大家推荐Javascript设计模式:观察者模式,无聊中的都看过来。

【 tulaoshi.com - Web开发 】

  一直以来都想把在实际开发中的一些经验系统地整理一下,共享出来给一些有志于深入B/S开发的朋友,趁着国庆忙里偷闲,先试验性的写一下有关于Javascript开发中的比较有用的东西。

  设计模式一直以来都是在面向对象的语言,或比较正规化的开发中才会出现的一门程序设计思想。一直以来众多的前端开发者认为使用OO方式来写JS更像是一个程序开发者,殊不知一旦将这门脚本语言深入进去后,仅仅才是一个开始,设计模式仅能算是对程序设计深入了解的第一步。

  为什么要用设计模式我就不多说了,仁者见仁的问题,现在还有一种反设计模式,如果有兴趣的话可以搜索一下相关的资料。

  观察者模式在程序设计里面的使用频率是很高的,与简单工厂模式,适配器模式基本相当,那么现在就直接进入主题。

  首先我们写一个最基本的HTML页面,包含一些基本的JS代码,目的很简单,当你输入一些字符到表单后,点击按钮,会改变相应的几个HTML对象的内容:

!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" target="_blank" rel="external"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd/a"html xmlns="a href="http://www.w3.org/1999/xhtml" target="_blank" rel="external"http://www.w3.org/1999/xhtml/a"headmeta http-equiv="Content-Type" content="text/html; charset=utf-8" /titleObserver Pattern - sample 01/titlescript type="text/javascript"//![CDATA[/*** Get Element by ID* @param {Object} id* @return Element*/function $(id){return document.getElementById(id);}/*** Window onload function*/window.onload = function(){var formObj = $("form1");formObj["changes"].onclick = function(){ $("Box_A").innerHTML = $("Box_A").id + ": "+formObj["fillbox"].value; $("Box_B").innerHTML = $("Box_A").id + ": "+formObj["fillbox"].value; $("Box_C").innerHTML = $("Box_A").id + ": "+formObj["fillbox"].value;}}//]]/script/headbodyform id="form1" div  input type="text" name="fillbox" size="50" /  input type="button" value="改变内容" name="changes" / /div/formdiv id="Box_A"Box_A/divdiv id="Box_B"Box_B/divdiv id="Box_C"Box_C/div/body/html

  上面都是最简单的面向过程的方法,这里有两点需要告诉大家的是:

  1.我们完全可以不再HTML里面直接写然后一个对象的时间方法(比如:a onclick="func();"/a),这样是完全没有必要的,因为我们可以使用动态方式绑定这些方法到对象上面。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/)

  2.在C/S开发中,每个程序总是要是有一个入口的,就是程序最初的触发方式,而所有的程序都是从这个地方开始,在代码的可读性上来说,这样做可以方便维护的时候快速找到程序入口,所以基本我是不会直接在JS里面去执行方法,而是使用window.onload事件来开始程序。当然更高级的技巧是使用DOM load方式,这样就可以避免大的图片载入前,你的JS什么都不能做的缺陷,具体的方式我会抽时间详细讲解B/S开发的入口设计。

  然后我们就要开始修改这些JS代码,最迟的修改肯定是把相同的方法给抽象出来,我们发现每个对象的innerHTML复制方式是一样的,那么我们就抽象这个方法出来:

  程序代码

/*** 改变内容* @param {Node} elem 节点对象* @param {String} value 值*/function ChangeContent(elem, value){  elem.innerHTML = elem.id + ": "+ value;}

  这个时候的onload方法就变为:

  程序代码

/*** Window onload function*/window.onload = function(){  var formObj = $("form1");  formObj["changes"].onclick = function(){    ChangeContent($("Box_A"),formObj["fillbox"].value);    ChangeContent($("Box_B"),formObj["fillbox"].value);    ChangeContent($("Box_C"),formObj["fillbox"].value);  }}

  当需求告诉我们,每个对象的内容需要显示不同的时候,我们就要把抽象方法按不同的需求重写一次,这个时候JS显示如下:

  程序代码

/*** Get Element by ID* @param {Object} id* @return Element*/function $(id){  return document.getElementById(id);}/*** Window onload function*/window.onload = function(){  var formObj = $("form1");  formObj["changes"].onclick = function(){    ChangeContentA($("Box_A"),formObj["fillbox"].value);    ChangeContentB($("Box_B"),formObj["fillbox"].value);    ChangeContentC($("Box_C"),formObj["fillbox"].value);  }}/**** @param {Object} elem* @param {Object} value*/function ChangeContentA(elem, value){  elem.innerHTML = elem.id + ": "+ value;}/**** @param {Object} elem* @param {Object} value*/function ChangeContentB(elem, value){  elem.innerHTML = elem.id + ": "+ value + "[我是Box B]";}/**** @param {Object} elem* @param {Object} value*/function ChangeContentC(elem, value){  elem.innerHTML = value;}

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/)

  大家可以发现我们在点击按钮的时候每次都需要去知道这3个对象是否存在,同时按照不同的对象调用不同的方法,因为实例非常简单,可以看到这些对象都在一起,而在实际开发中,这种可能性就很小了,而且很有可能这些对象都是动态创建的,那么我们不可能事先就知道这个对象是否存在,而每次都要去判断对象是否存在就觉得很麻烦,那么观察者模式在这里就可以派上用场了,首先我们先了解一下这个模式的说明:

  概述

  在软件构建过程中,我们需要为某些对象建立一种通知依赖关系 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

  意图

  定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[GOF 《设计模式》]

  结构图

  

  代码实现如下:

  程序代码

/*** Get Element by ID* @param {Object} id* @return Element*/function $(id){  return document.getElementById(id);}/*** Window onload function*/window.onload = function(){  /**   * 内容观察者   */  var ContentObserver = new Observer();//申明一个观察者  ContentObserver.register("Item.Box_A", $("Box_A"), ChangeContentA);  //ContentObserver.unRegister("Item.Box_A"); //卸载Box A  ContentObserver.register("Item.Box_B", $("Box_B"), ChangeContentB);  ContentObserver.register("Item.Box_C", $("Box_C"), ChangeContentC);  var formObj = $("form1");  formObj["changes"].onclick = formObj["fillbox"].onkeyup = function(){    ContentObserver.notify(formObj["fillbox"].value);  }}/**** @param {Object} elem* @param {Object} value*/function ChangeContentA(elem, value){  elem.innerHTML = elem.id + ": "+ value;}/**** @param {Object} elem* @param {Object} value*/function ChangeContentB(elem, value){  elem.innerHTML = elem.id + ": "+ value + "[我是Box B]";}/**** @param {Object} elem* @param {Object} value*/function ChangeContentC(elem, value){  elem.innerHTML = value;}/*** 刪除數組子对象* @param {String} attrName 属性名称* @param {String} value 属性值* @return Object*/Array.prototype.RemoveObject = function(attrName, value) {  for (var i = 0, j = this.length; i  j; i++) {    if (this[i][attrName] == value) {      return this.splice(i, 1);    }  }};/*** 观察者*/function Observer(){  /**   * 注册对象列表   */  this.groups = [];  /**   * 注册对象   * @param {String} groupName 名称   * @param {Node} elem 节点对象   * @param {Function} callback 回调方法   */  this.register = function(groupName, elem, callback){    this.unRegister(groupName);//防止重复注册    //加入这个对象到对象列表    this.groups.push({ Name: groupName, Provider: elem, Func: callback });  };  /**   * 卸载对象   * @param {Object} groupName 名称   */  this.unRegister = function(groupName){    this.groups.RemoveObject("Name",groupName);  };  /**   * 通知改变   * @param {Object} attchs 消息对象 [Name:名称 Provider:节点对象 Func:回调方法]   */  this.notify = function(attchs){    //批量执行注册对象的回调方法    for (var i = 0, j = this.groups.length; i  j; i++) {      this.groups[i].Func(this.groups[i].Provider, attchs);    }  };}

  关键在于Observer这个观察者对象上,大家都看到他有3个方法:注册,反注册,通知。

  而每个对象在onload事件中会进行注册,然后当我们点击按钮,或者直接在输入框输入内容时,只需要执行一个通知方法,那么所有有关的对象都会自动调用不同的方法,至于好处大家都闭上眼睛YY一下。

  全部代码:

!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" target="_blank" rel="external"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd/a"html xmlns="a href="http://www.w3.org/1999/xhtml" target="_blank" rel="external"http://www.w3.org/1999/xhtml/a"headmeta http-equiv="Content-Type" content="text/html; charset=utf-8" /titleObserver Pattern - sample 04/titlescript type="text/javascript"//![CDATA[/*** Get Element by ID* @param {Object} id* @return Element*/function $(id){return document.getElementById(id);}/*** Window onload function*/window.onload = function(){/** * 内容观察者 */var ContentObserver = new Observer();//申明一个观察者ContentObserver.register("Item.Box_A", $("Box_A"), ChangeContentA);//ContentObserver.unRegister("Item.Box_A"); //卸载Box AContentObserver.register("Item.Box_B", $("Box_B"), ChangeContentB);ContentObserver.register("Item.Box_C", $("Box_C"), ChangeContentC);var formObj = $("form1");formObj["changes"].onclick = formObj["fillbox"].onkeyup = function(){ ContentObserver.notify(formObj["fillbox"].value);}}/**** @param {Object} elem* @param {Object} value*/function ChangeContentA(elem, value){elem.innerHTML = elem.id + ": "+ value;}/**** @param {Object} elem* @param {Object} value*/function ChangeContentB(elem, value){elem.innerHTML = elem.id + ": "+ value + "[我是Box B]";}/**** @param {Object} elem* @param {Object} value*/function ChangeContentC(elem, value){elem.innerHTML = value;}/*** 刪除數組子对象* @param {String} attrName 属性名称* @param {String} value 属性值* @return Object*/Array.prototype.RemoveObject = function(attrName, value) {for (var i = 0, j = this.length; i  j; i++) { if (this[i][attrName] == value) { return this.splice(i, 1); }}};/*** 观察者*/function Observer(){/** * 注册对象列表 */this.groups = [];/** * 注册对象 * @param {String} groupName 名称 * @param {Node} elem 节点对象 * @param {Function} callback 回调方法 */this.register = function(groupName, elem, callback){ this.unRegister(groupName);//防止重复注册 //加入这个对象到对象列表 this.groups.push({ Name: groupName, Provider: elem, Func: callback });};/** * 卸载对象 * @param {Object} groupName 名称 */this.unRegister = function(groupName){ this.groups.RemoveObject("Name",groupName);};/** * 通知改变 * @param {Object} attchs 消息对象 [Name:名称 Provider:节点对象 Func:回调方法] */this.notify = function(attchs){ //批量执行注册对象的回调方法 for (var i = 0, j = this.groups.length; i  j; i++) { this.groups[i].Func(this.groups[i].Provider, attchs); }};}//]]/script/headbodyform id="form1" div  input type="text" name="fillbox" size="50" /  input type="button" value="改变内容" name="changes" / /div/formdiv id="Box_A"Box_A/divdiv id="Box_B"Box_B/divdiv id="Box_C"Box_C/div/body/html

  适用性

  1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

来源:http://www.tulaoshi.com/n/20160219/1612514.html

延伸阅读
描述: 计数代理模式在客户对象调用服务提供者对象上方法的前后执行诸如日志(logging)和计数(counting)一系列附加功能时很有用。 !-- frame contents -- !-- /frame contents -- 计数代理模式建议把这些附加功能封装在一个单独的对象,这个对象就是指计数代理对象,而不是把这些附加的功能实现放到服务提供者的内部。...
标签: Web开发
最近在读《JavaScript 设计模式》一书,其中工厂模式中提到了memoizing技术,今天仔细整理了一下memoization 相关的资料,与大家共享。 Memoization 的定义: memoization 一词是Donald Michie 根据拉丁语memorandum杜撰的一个词。相应的动词、过去分词、ing形式有memoiz、memoized、memoizing. Memoization 是一种将函数返回值缓...
标签: Java JAVA基础
一:介绍 记得初学jsp的时候,总喜欢把他和asp,php去比较,也习惯于使用asp的开发模式去写jsp后来才发现这真是很傻的做法,其实jsp一出了来就已经用MVC模式了。下面我就简要说说jsp设计使用MVC来设计。 二:MVC介绍 MVC其实是模、视图、控制的缩写,也就是说在使用jsp时,有相应的文件去实现相应的操作 通常jsp只负责v...
标签: PHP
转自:http://www.cnblogs.com/team/DesignPattern.html 1、 FACTORY 一追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说来四个鸡翅就行了。麦当劳和肯德基就是生产鸡翅的Factory     工厂模式 :客户类和工厂类分开。消费...
很多的J2EE应用程序需要使用持久性数据(数据库、文件等)。不同的程序,持久性存储是各不相同的,并且用来访问这些不同的持久性存储机制的API也有很大的不同。假如应用程序要在不同的持久性存储间迁移,这些访问特定持久存储层的代码将面临重写。 !-- frame contents -- !-- /frame contents -- 如何解决这个问题?且...

经验教程

91

收藏

43
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部