Javascript乱弹设计模式系列在开始设计模式的书写之前,有必要对Javascript面向对象的概念先做个介绍,那么这篇文章就以面向对象基础作为起点吧。理论知识首先Javascript是弱类型语言,它定义变量时不必声明类型,如varPersfunctifunctipublicinterfacePersvarPerson=newInterface(Person,[[getName,0],[setName,1]]);其中Interface类是稍后要说的接口类,第一个参数Person是接口类的名称,第二个参数是个二维数组,getName是接口方法的名称,0是该方法所带的参数个数(因为Javascript是弱语言,所以类型是不确定的,所以只要记住参数个数就好,0可以省略不写),setName同理。这样一个接口定义好了。怎样使用它呢?前言博客园谈设计模式的文章很多,我也受益匪浅,包括TerryLee、吕震宇等等的.NET设计模式系列文章,强烈推荐。对于我,擅长于前台代码的开发,对于设计模式也有一定的了解,于是我想结合Javascript来设计前台方面的“设计模式”,以对后台“设计模式”做个补充。开始这个系列我也诚惶诚恐,怕自己写得不好,不过我也想做个尝试,一来希望能给一些人有些帮助吧,二来从写文章中锻炼下自己,三来通过写文章对自己增加自信;如果写得不好,欢迎拍砖,我会虚心向博客园高手牛人们学习请教;如果觉得写得还可以,谢谢大家的支持了:)这篇将介绍观察者模式。概述在现实生活中,存在着“通知依赖关系”,如在报纸订阅的服务,只要读者(订阅者)订购了《程序员》的期刊杂志,那么他就订阅了这个服务,他时刻“监听”着邮递员(出版者)来投递报纸给他们,而邮递员(出版者)只要报社有新刊杂志传达给他(就是状态发生了变化),邮递员(出版者)就随时投递(通知)订阅了服务的读者;另一方面,如果读者不想在继续订购(取消通知)《程序员》的杂志了,那么邮递员就不在投递(通知)这些读者了。---这就是典型的出版者和订阅者的关系,而这个关系用一个公式来概括:出版者+订阅者=观察者模式定义观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。类图示例分析现在开始利用观察者模式来应用到项目的一个场景中去,并且层层剖析一下。有这样一个场景,一个购书网站,用户提交上去一个订单,网站系统只要有订单上来,会采取如下操作:(为了简化,我这里其实只是简单的提交)一、产生一条通知用户“已购买”记录的短信息(该短信箱还会有其他记录,如交友等等);二、在浏览器上显示你的订单名片;三、该条订单提交上服务器,保存到数据库或者其它任何存储介质中去,最后显示你的购书记录;那么开始我的设计:1.网站上添加IPublisher.js,它作为系统的出版者“接口”,利用第0篇文章面向对象基础以及接口和继承类的实现中的Interface.js类(另外,谢谢winter-cn园友提出了些宝贵的建议,目前Interface类还在改善中,这里暂且先用原来的Interface类:这里是改进的程序示范,包括重载函数的构造,这里也暂时贴出来下:改进的代码//Interface.jsfunctionInterface(name,methods){if(arguments.length!=2){thrownewError(接口构造函数含+arguments.length+个参数,但需要2个参数.);}this.name=name;this.methods=[];if(methods.length1){thrownewError(第二个参数为空数组.);}for(vari=0,len=methods.length;ilen;i++){if(typeofmethods[i][0]!=='string'){thrownewError(接口构造函数第一个参数必须为字符串类型.);}for(varj=1;jmethods[i].length;j++){if(methods[i][j]&&typeofmethods[i][j]!=='number'){thrownewError(接口构造函数第二个参数以上必须为整数类型.);}}this.methods.push(methods[i]);}};Interface.registerImplements=function(object){if(arguments.length2){thrownewError(接口的实现必须包含至少2个参数.);}for(vari=1,len=arguments.length;ilen;i++){varinterface=arguments[i];if(interface.constructor!==Interface){thrownewError(从第2个以上的参数必须为接口实例.);}for(varj=0,methodsLen=interface.methods.length;jmethodsLen;j++){varmethod1=interface.methods[j][0];vararr1=interface.methods[j].slice(1).sort(compareNumber);for(vark=0;kobject.methodArr.length;k++){varmethod2=object.methodArr[k][0];if(method1===method2){vararr2=object.methodArr[k].slice(1).sort();if(ComareArray(arr1,arr2)){break;}else{thrownewError(接口的实现对象不能执行+interface.name+的接口方法+method1+,因为它找不到或者不匹配.);}}}}}};functioncompareNumber(num1,num2){variNum1=parseInt(num1);variNum2=parseInt(num2);if(iNum1iNum2)return-1;elseif(iNum1iNum2)return1;elsereturn0;}functionComareArray(arr1,arr2){if(arr1.length!=arr2.length){returnfalse;}for(vari=0;iarr1.length;i++){if(arr1[i]!==arr2[i]){returnfalse;}}returntrue;}Function.prototype.getParameters=function(){varstr=this.toString();varparamString=str.slice(str.indexOf('(')+1,str.indexOf(')')).replace(/\s*/g,'');//取得参数字符串try{return(paramString.length==0?[]:paramString.split(','));}catch(err){thrownewError(函数不合法!);}}//demo.jsfunctionOverload(method){this.methods=[];for(vari=1;i=arguments.length-1;i++){methods.push(arguments[i].length);}OverloadNumber.methodArr.push([arguments[0]].concat(methods));OverloadNumber.argumentArr.push(arguments);returnfunction(){for(vari=0;iOverloadNumber.methodArr.length;i++){if(OverloadNumber.methodArr[i][0]==method){varindex=OverloadNumber.methodArr[i].slice(1).indexOf(arguments.length);if(index!=-1){returnOverloadNumber.argumentArr[i][index+1].apply(this,arguments);}}}thrownewError(参数不匹配!);}}varINumber=newInterface(INumber,[[Add,1,0,2,3],[Sub,1,2]]);//其中1,0,2,3之类属于重载函数参数个数,可以不按先后顺序functionOverloadNumber(){Interface.registerImplements(OverloadNumber,INumber);}OverloadNumber.methodArr=[];OverloadNumber.argumentArr=[];OverloadNumber.prototype={Add:Overload(//算术加Add,function(a,b){returna+b;},function(a){return++a;},function(a,b,c){returna+b+c;},function(){return-1;}),Sub:Overload(//算术减Sub,function(a){return--a;},function(a,b){returna-b;})}调用如下:varnumber=newOverloadNumber();alert(4-1=+number.Sub(4,1));alert(++3=+number.Add(3));alert(4+6=+number.Add(4,6));alert(4+6+5=+number.Add(4,6,5));alert(number.Add());alert(3-1=+number.Sub(3,1));alert(--10=+number.Sub(10));alert(number.Add(4,6,5,9));//Error!参数不匹配)--------------------varIPublisher=newInterface('IPublisher',[['registerSubscriber',1],['removeSubscriber',1],['notifySubscribers']]);所有的依赖者(订阅者)将要注册于它的实现类。2.添加ISubscriber.js,它作为系统的订阅者“接口”:varISubscriber=newInterface('ISubscriber',[['update',4]]);3.现在开始实现我们的IPublisher的具体类,添加OrderData.js,它作为一个订单数据类,让其继承IPublisher的接口:functionOrderData(){this._subscribers=newArray();//观察者列表this.ProductName=;//商品名称this.ProductPrice=0.0;//商品价格this.Recommend=0;//推荐指数this.productCount=0;//购买个数Interface.registerImplements(this,IPublisher);}OrderData.prototype={registerSubscriber:function(subscriber){//注册订阅者this._subscribers.push(subscriber);},removeSubscriber:function(subscriber){//删除指定订阅者vari=_subscribers.indexOf(subscriber);if(i0)_subscribers.slice(i,1);},not