装饰者模式

可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰者模式。

代码例子

     var Plane = function(){}
Plane.prototype.fire = function(){
console.log( '发射普通子弹' );
}
var one = function( plane ){ //装饰类 one
this.plane = plane;
}
one.prototype.fire = function(){
this.plane.fire();
console.log( '发射导弹' );
}
var two = function( plane ){ //装饰类 two
this.plane = plane;
}
two.prototype.fire = function(){
this.plane.fire();
console.log( '发射原子弹' );
} var plane = new Plane();
plane = new one( plane ); //装饰对象会覆盖原来的对象,这么做是因为装饰者模式是为了添加职能,可能这时候的代码量已经很大了,原对象的方法可能在很多地方都调用过,所以我们通过这种方式,追加了功能而不用修改太多代码。
plane = new two( plane ); /**装饰对象调用**/
plane.fire(); /* 发射普通子弹
发射导弹
发射原子弹
*/

从代码我们可以看出,装饰者模式这种给对象动态增加职责的方式,并没有真正地改动对象自身。

从形式上来看,装饰者模式就好像给原来的对象包裹了一层,产生一个新的对象,同时,保留了接口统一。装饰类的作用就是接受一个对象,然后把它变成功能更多的对象。

装饰类接收原来的对象,会把它保存下来,调用装饰类时,会将本来的对象放入另一个对象之中,这些对象以一条链的方式进行引用,形成一个聚合对象。这些对象都拥有相同的接口(fire方法),当请求达到链中的某个对象时,这个对象会执行自身的操作,随后把请求转发给链中的下一个对象。

从传递请求这一点来看有点类似于职责链,但是职责链是传递请求给正确执行的对象,而这个不但自身会执行,通过装饰类添加的方法也会执行,它这种类似于链子一样的请求调用与其说是链子倒不如说是包裹更合适。装饰类会把原来的对象包裹起来,调用时会一层一层的调用。

更js的实现方法

因为js是动态语言,修改类型,改变对象的属性和方法对它来说太简单了,所以还有一种思路,我们拿到需要调用的方法的引用,然后直接对它进行改造即可。不过这种方式等于直接修改原对象的方法,和上面的方法哪个更好,我说不上来。

     var plane = {      //原对象
fire: function(){
console.log( '发射普通子弹' );
}
}
var add_one = function(){ //想要添加的功能
console.log( '发射导弹' );
}
var add_two = function(){
console.log( '发射原子弹' );
} var fire1 = plane.fire; //存储对象方法的引用
plane.fire = function(){ //改写对象方法
fire1(); //原对象的方法
add_one(); //添加的方法
} var fire2 = plane.fire; //同上
plane.fire = function(){
fire2();
add_two();
} plane.fire(); /* 发射普通子弹
发射导弹
发射原子弹
*/

另外我还有一个不成熟的想法,像第一种实现装饰者的方法里,我们覆盖了原来对象的变量。使得所有调用对应方法的位置都会执行添加的函数,但是如果我们不是全都需要而是有的地方需要有的地方不需要呢?

我有两个思路,一个是在装饰类里重写方法时进行判断,另一个是把原来的对象单独给个变量,和装饰类加工过的对象有所区分,不需要执行新功能的地方,就用原来的对象调用。

感觉好像第一个思路好一点?

装饰函数

在js中,函数也是对象,只要是对象自然可以实现装饰者。

     var a=function(){
alert(1);
} var _a=a; //保存函数引用 a=function(){ //重写函数
_a(); //原本的函数引用
alert(2); //新添加的功能
}

又比如我们想给window绑定onload事件,但是又不确定这个事件是不是已经被其他人绑定过,为了避免覆盖掉之前的window.onload函数中的行为,我们一般都会先保存好原先的window.onload,把它放入新的window.onload 里执行:

     window.onload = function(){
alert (1);
}
var _onload = window.onload || function(){}; //没有绑过onload,让变量为空的函数 window.onload = function(){ //重写
_onload();
alert (2); //追加的代码
}

可能的坑:this

保存引用再重写,可能会带来this绑定错误的问题,因为我们保存的引用中的this和当前执行环境的this可能完全不同了,重写的函数有时候上下文其实已经改变了,如果上下文改变,this的指向也就改变了,所以它有可能没有指向正确的对象。

     var _getElementById = document.getElementById;  //这个是document这个对象的方法,其内部this,指向document,我们把它的引用保存到_getElementById这个全局变量里
document.getElementById = function( id ){ //重写函数
alert (1);
return _getElementById( id ); // 返回了保留的函数,但是返回的函数是全局函数,this指向window,没有指向正确的document对象
}
var button = document.getElementById( 'button' ); //报错,Uncaught TypeError: Illegal invocation

又比如:

     var name="大白";
var a={
name:"如花",
getname:function(){
console.log(this.name);
}
} var get=a.getname; //保存引用 get(); //大白
a.getname(); //如花

可以看到,调用函数时的环境改变,虽然保存了引用,却没得到我们想要的结果。(我们想获得如花)

如果遇到这种情况,也就只能手动更改(apply),或者控制在同一个上下文环境中添加职责。

比如上面的代码,我们改成:

    get.apply(a);  //如花

就可以获得正确的结果。

第一个例子也是同理

     var _getElementById = document.getElementById;
document.getElementById = function(){
alert (1);
return _getElementById.apply( document, arguments ); //apply大显身手,指定上下文为document
}
var button = document.getElementById( 'button' );

优缺点

装饰者模式可以说是AOP思想的实例。优点:解耦,追加功能不用修改原本函数,更为灵活方便。缺点:会叠加函数的作用域,如果装饰的链条过长,性能上也会受到一些影响。

《javascript设计模式与开发实践》阅读笔记(15)—— 装饰者模式的更多相关文章

  1. javascript设计模式与开发实践阅读笔记(4)——单例模式

    定义 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 具体来说,就是保证有些对象有且只有一个,比如线程池.全局缓存.浏览器中的window 对象等.在js中单例模式用途很广,比如登录 ...

  2. javascript设计模式与开发实践阅读笔记(8)——观察者模式

    发布-订阅模式,也叫观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript开发中,我们一般用事件模型来替代传统的观察者模式. ...

  3. javascript设计模式与开发实践阅读笔记(7)——迭代器模式

    迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  4. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

  5. javascript设计模式与开发实践阅读笔记(5)——策略模式

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...

  6. javascript设计模式与开发实践阅读笔记(9)——命令模式

    命令模式:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 说法很复 ...

  7. javascript设计模式与开发实践阅读笔记(11)—— 模板方法模式

    模板方法模式: 由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类.通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序.子类通过继承这个抽象类,也继 ...

  8. JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)

    说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...

  9. 《JavaScript设计模式与开发实践》笔记第八章 发布-订阅模式

    第八章 发布-订阅模式 发布-订阅模式描述 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于 ...

  10. JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)

    上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值, ...

随机推荐

  1. 通过logstash-input-mongodb插件将mongodb数据导入ElasticSearch

    目的很简单,就是将mongodb数据导入es建立相应索引.数据是从特定的网站扒下来,然后进行二次处理,也就是数据去重.清洗,接着再保存到mongodb里,那么如何将数据搞到ElasticSearch中 ...

  2. C++学习-10

    类型转换:自动转换,显示转换,强转 总结:CPP编译的时候,从上往下,遇到不匹配,声明扩展了一个类的作用域(此时最多只能创建类的指针或者引用),         由于没有定义是不可能使用到类的成员 完 ...

  3. sprintf函数使用

    功能 把格式化的数据写入某个字符缓冲区. 所需头文件 stdio.h 原型 int sprintf( char *buffer, const char *format, [ argument] - ) ...

  4. 关于LINUX里面查找,替换,编辑的一些用法

    1.GREP查找(如果只是查找文件当中是否有该内容的话) grep -rn "hello,world!" * "hello,world!":表示要查找的字段 * ...

  5. Scala学习笔记(一)

    scala 版HelloWorrld object HelloWorld{ def main(args:Array[String]){ println("Hello World!!!&quo ...

  6. 正则表达式入门+实战(c#实现)

    如果有人和你说,如果不将字符串转换为数字,你要如何判断字符串是否由全数字组成?把字符串拆成char数组,然后放入一个循环,来判断每个char是否为数字?那你要如何判断手机号是否合法?IP是否合法呢?把 ...

  7. Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录

    1.登录的实现 登录功能实现起来有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,当然还有其他模式,今天主要探讨一下在Asp.net core 2.0下 ...

  8. 前端的UI设计与交互之字体篇

    跨平台的字体设定,力求在各个操作系统下都有最佳展示效果.字体是界面设计中最重要的基本构成之一,用户通过文本来消化内容和完成工作,优雅的字体将大大提升用户的阅读体验及工作效率.在满足不同终端始终保持良好 ...

  9. npm包使用语义化版本号

    npm 采用语义版本管理软件包.所谓语义版本,就是指版本号为a.b.c的形式,其中a是大版本号,b是小版本号,c是补丁号. 一个软件发布的时候,默认就是1.0.0版.如果以后发布补丁,就增加最后一位数 ...

  10. Redis set集合结构及命令详解

    set 无序集合 集合的性质: 唯一性,无序性,确定性 注: 在string和link的命令中,可以通过range 来访问string中的某几个字符或某几个元素 但,因为集合的无序性,无法通过下标或范 ...