深入理解JavaScript系列(29):设计模式之装饰者模式详解


Posted in Javascript onMarch 03, 2015

介绍

装饰者提供比继承更有弹性的替代方案。 装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数)。

装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。

正文

那么装饰者模式有什么好处呢?前面说了,装饰者是一种实现继承的替代方案。当脚本运行时,在子类中增加行为会影响原有类所有的实例,而装饰者却不然。取而代之的是它能给不同对象各自添加新行为。如下代码所示:

//需要装饰的类(函数)

function Macbook() {

    this.cost = function () {

        return 1000;

    };

}
function Memory(macbook) {

    this.cost = function () {

        return macbook.cost() + 75;

    };

}
function BlurayDrive(macbook) {

    this.cost = function () {

        return macbook.cost() + 300;

    };

}


function Insurance(macbook) {

    this.cost = function () {

        return macbook.cost() + 250;

    };

}


// 用法

var myMacbook = new Insurance(new BlurayDrive(new Memory(new Macbook())));

console.log(myMacbook.cost());

下面是另一个实例,当我们在装饰者对象上调用performTask时,它不仅具有一些装饰者的行为,同时也调用了下层对象的performTask函数。

function ConcreteClass() {

    this.performTask = function () {

        this.preTask();

        console.log('doing something');

        this.postTask();

    };

}
function AbstractDecorator(decorated) {

    this.performTask = function () {

        decorated.performTask();

    };

}
function ConcreteDecoratorClass(decorated) {

    this.base = AbstractDecorator;

    this.base(decorated);
    decorated.preTask = function () {

        console.log('pre-calling..');

    };
    decorated.postTask = function () {

        console.log('post-calling..');

    };
}
var concrete = new ConcreteClass();

var decorator1 = new ConcreteDecoratorClass(concrete);

var decorator2 = new ConcreteDecoratorClass(decorator1);

decorator2.performTask();

再来一个彻底的例子:

var tree = {};

tree.decorate = function () {

    console.log('Make sure the tree won\'t fall');

};
tree.getDecorator = function (deco) {

    tree[deco].prototype = this;

    return new tree[deco];

};
tree.RedBalls = function () {

    this.decorate = function () {

        this.RedBalls.prototype.decorate(); // 第7步:先执行原型(这时候是Angel了)的decorate方法

        console.log('Put on some red balls'); // 第8步 再输出 red

        // 将这2步作为RedBalls的decorate方法

    }

};
tree.BlueBalls = function () {

    this.decorate = function () {

        this.BlueBalls.prototype.decorate(); // 第1步:先执行原型的decorate方法,也就是tree.decorate()

        console.log('Add blue balls'); // 第2步 再输出blue

        // 将这2步作为BlueBalls的decorate方法

    }

};
tree.Angel = function () {

    this.decorate = function () {

        this.Angel.prototype.decorate(); // 第4步:先执行原型(这时候是BlueBalls了)的decorate方法

        console.log('An angel on the top'); // 第5步 再输出angel

        // 将这2步作为Angel的decorate方法

    }

};
tree = tree.getDecorator('BlueBalls'); // 第3步:将BlueBalls对象赋给tree,这时候父原型里的getDecorator依然可用

tree = tree.getDecorator('Angel'); // 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用

tree = tree.getDecorator('RedBalls'); // 第9步:将RedBalls对象赋给tree
tree.decorate(); // 第10步:执行RedBalls对象的decorate方法

总结

装饰者模式是为已有功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的函数里,然后用该函数包装所要装饰的已有函数对象,因此,当需要执行特殊行为的时候,调用代码就可以根据需要有选择地、按顺序地使用装饰功能来包装对象。优点是把类(函数)的核心职责和装饰功能区分开了。

Javascript 相关文章推荐
为JavaScript添加重载函数的辅助方法
Jul 04 Javascript
javascript 快速排序函数代码
May 30 Javascript
让JavaScript和其它资源并发下载的方法
Oct 16 Javascript
js实现模拟计算器退格键删除文字效果的方法
May 07 Javascript
jQuery中serializeArray()与serialize()的区别实例分析
Dec 09 Javascript
ECMAScript6快速入手攻略
Jul 18 Javascript
解析AngularJS中get请求URL出现的跨域问题
Dec 01 Javascript
vue 点击按钮增加一行的方法
Sep 07 Javascript
微信小程序后端(java)开发流程的详细步骤
Nov 13 Javascript
vue实现导航菜单和编辑文本的示例代码
Jul 04 Javascript
微信小程序input抖动问题的修复方法
Mar 03 Javascript
分享15个Webpack实用的插件!!!
Mar 31 Javascript
jQuery对象与DOM对象之间的相互转换
Mar 03 #Javascript
深入理解JavaScript系列(28):设计模式之工厂模式详解
Mar 03 #Javascript
JS运动基础框架实例分析
Mar 03 #Javascript
jQuery DOM插入节点操作指南
Mar 03 #Javascript
JS运动框架之分享侧边栏动画实例
Mar 03 #Javascript
jQuery DOM删除节点操作指南
Mar 03 #Javascript
JS实现表格数据各种搜索功能的方法
Mar 03 #Javascript
You might like
PHILIPS D1835/D1875的电路分析与打理
2021/03/02 无线电
BBS(php & mysql)完整版(三)
2006/10/09 PHP
mysql建立外键
2006/11/25 PHP
PHP生成网页快照 不用COM不用扩展.
2010/02/11 PHP
PHP验证码函数代码(简单实用)
2013/09/29 PHP
php获取淘宝分类id示例
2014/01/16 PHP
PHP在同一域名下两个不同的项目做独立登录机制详解
2017/09/22 PHP
Laravel使用swoole实现websocket主动消息推送的方法介绍
2019/10/20 PHP
怎么让脚本或里面的函数在所有图片都载入完毕的时候执行
2006/10/17 Javascript
BOM与DOM的区别分析
2010/10/26 Javascript
50个比较实用jQuery代码段
2011/09/18 Javascript
使用js获取地址栏中传递的值
2013/07/02 Javascript
禁止空格提交表单的js代码
2013/11/17 Javascript
JS中获取函数调用链所有参数的方法
2015/05/07 Javascript
IE8下jQuery改变png图片透明度时出现的黑边
2015/08/30 Javascript
jquery trigger实现联动的方法
2016/02/29 Javascript
JS面向对象编程详解
2016/03/06 Javascript
js轮盘抽奖实例分析
2020/04/17 Javascript
原生js封装的一些jquery方法(详解)
2016/09/20 Javascript
jQGrid动态填充select下拉框的选项值(动态填充)
2016/11/28 Javascript
详解vue.js+UEditor集成 [前后端分离项目]
2017/07/07 Javascript
JavaScript正则表达式函数总结(常用)
2018/02/22 Javascript
微信小程序学习笔记之函数定义、页面渲染图文详解
2019/03/28 Javascript
vue中使用微信公众号js-sdk踩坑记录
2019/03/29 Javascript
jQuery实现全选、反选和不选功能的方法详解
2019/12/04 jQuery
[41:05]Serenity vs Pain 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
[54:53]完美世界DOTA2联赛PWL S2 GXR vs PXG 第二场 11.18
2020/11/18 DOTA
对python同一个文件夹里面不同.py文件的交叉引用方法详解
2018/12/15 Python
Python 3.8 新功能大揭秘【新手必学】
2020/02/05 Python
Python unittest单元测试openpyxl实现过程解析
2020/05/27 Python
解决pytorch多GPU训练保存的模型,在单GPU环境下加载出错问题
2020/06/23 Python
Python新建项目自动添加介绍和utf-8编码的方法
2020/12/26 Python
澳洲健康食品网上商店:Aussie Health Products
2018/06/15 全球购物
美国伊甸园兄弟种子公司:Eden Brothers
2018/07/01 全球购物
中国式结婚:司仪主持词(范文)
2019/07/25 职场文书
sql server 累计求和实现代码
2022/02/28 SQL Server