设计模式笔记(五):JavaScript中的装饰者模式

一 装饰者模式的定义 装饰者模式的定义:动态地给一个对象添加额外的职责,而不会影响从这个类派生的其他对象。 二 装饰者模式的

一. 装饰者模式的定义

装饰者模式的定义:动态地给一个对象添加额外的职责,而不会影响从这个类派生的其他对象。

二. 装饰者模式的实现

装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对象动态添加职责。

在JavaScript的实际实现中,可以将本体对象放到装饰者对象中,这些对象以一条链的方式进行引用,形成一个聚合对象。本体对象和装饰者对象都拥有相同的接口,当请求到达链中的装饰者对象,这个装饰者对象会执行自身,随后将请求转发给本体对象。

2.1 通过对象覆写来实现装饰者模式

代码如下:

/* =============== 本体对象 =============== */var Person = function() {};Person.prototype.work = function() { console.log("工作");};/* =============== 装饰者对象 =============== *//* * 装饰者对象:程序员 */var ProgramerDecorator = function(person) { this.person = person;};ProgramerDecorator.prototype.work = function() { this.person.work(); // 执行本体对象的方法 // 执行装饰者对象自身的方法 console.log("写程序");};/* * 装饰者对象:厨师 */var ChiefDecorator = function(person) { this.person = person;};ChiefDecorator.prototype.work = function() { this.person.work(); // 执行本体对象的方法 // 执行装饰者对象自身的方法 console.log("炒菜");};/* =============== 客户端调用 =============== */var person = new Person();person = new ProgramerDecorator(person);person.work(); // 分别输出:工作、写程序

因为装饰者和它所装饰的本体对象拥有一致的接口,所以他们对使用该对象的用户来说是透明的。被装饰的本体对象也并不需要了解它是否曾被装饰过。这种透明性使我们可以递归地嵌套任意多个装饰者对象。例如:

/* =============== 客户端调用 =============== */var person = new Person();person = new ProgramerDecorator(person);person = new ChiefDecorator(person);person.work(); // 分别输出:工作、写程序、炒菜

装饰者模式将本体对象嵌入到装饰者对象中,实际相当于本体对象被装饰者对象包装起来,形成一条包装链。请求随着这条链依次传递到所有对象,每个对象都有处理这个请求的机会。

2.2 改良版:使用构造函数的静态属性实现装饰者模式

上面的装饰者模式通过重写对象来实现。但是这样会有一个很大的问题:会导致原型链的丢失。使得最开始的Person类中的其他方法和属性可能丢失。

下面我们使用构造函数的静态属性来实现装饰者模式,使原型链得以延续。代码如下:

/* =============== 本体对象 =============== */var Person = function() {};Person.prototype.work = function() { console.log("工作");};// 装饰者对象的调用继承实现Person.prototype.decorate = function(decorator) { var F = function() {}, overrides = this.constructor.decorators[decorator], i, newObj; F.prototype = this; newObj = new F(); newObj.super = F.prototype; for(i in overrides) { if(overrides.hasOwnProperty(i)) { newObj[i] = overrides[i]; } } return newObj;};/* =============== 装饰者对象 =============== */Person.decorators = {};/* * 装饰者对象:程序员 */Person.decorators.programer = { work: function() { this.super.work(); console.log("写程序"); }};/* * 装饰者对象:厨师 */Person.decorators.chief = { work: function() { this.super.work(); console.log("炒菜"); }};/* =============== 客户端调用 =============== */var person = new Person();person = person.decorate("programer");person = person.decorate("chief");person.work(); // 分别输出:工作、写程序、炒菜

这样,新装饰的对象newObj将继承目前所有用的对象(无论是原始对象,还是已经添加了最后的装饰者的对象),以便于子对象可以访问父对象。

2.3 进化版:使用列表实现装饰者模式

通过将装饰者对象包装到列表中,可以考虑不需要使用继承,利用JavaScript的动态性质,来实现装饰者模式。代码如下:

/* =============== 本体对象 =============== */var Person = function() { this.decorators_list = []; // 装饰者对象列表};Person.prototype.work = function() { console.log("工作"); // 本体对象方法 this.renderDecorator("work"); // 渲染装饰者对象方法};// 装饰者对象列表动态添加Person.prototype.decorate = function(decorator) { this.decorators_list.push(decorator);};// 遍历装饰者对象列表,并渲染装饰者对象的对应方法Person.prototype.renderDecorator = function(method) { var decoratorsList = this.decorators_list, decorators = this.constructor.decorators, len = decoratorsList.length; for(var i = 0; i < len; i++) { (function(i) { var name = decoratorsList[i], decorator = decorators[name]; if(typeof decorator[method] == "function") { decorator[method](); } })(i); }};/* =============== 装饰者对象 =============== */Person.decorators = {};/* * 装饰者对象:程序员 */Person.decorators.programer = { work: function() { console.log("写程序"); }};/* * 装饰者对象:厨师 */Person.decorators.chief = { work: function() { console.log("炒菜"); }};/* =============== 客户端调用 =============== */var person = new Person();person.decorate("programer");person.decorate("chief");person.work(); // 分别输出:工作、写程序、炒菜

通过decorators_list列表来存储装饰者对象,等到调用方法时,才去遍历该装饰者对象列表中的装饰方法。这样的实现更简单,并且也不涉及继承。

三. 装饰者模式与代理模式的异同点

装饰者模式和代理模式很相像,都描述了怎样为对象提供一定程度上的间接引用,他们的实现部分都保留了对另一个对象的引用,并且向那个对象发送请求。

代理模式和装饰者模式的最重要的区别在于他们的意图和设计目的:

  • 代理模式的目的是,当直接访问本体不方便时,为这个本体提供一个替代者。本体定义了关键功能,而代理提供或拒绝对他的访问,或者在访问本体之前做一些额外的事情。装饰者模式的作用是为对象动态添加职责。
  • 代理模式强调的是(代理与本体对象之间的关系),并且这种关系一开始就被可以确定。而装饰者模式用于一开始不能确定对象的全部功能时。
  • 代理模式通常只有一层代理-本体的引用。而装饰者模式经常会形成一条长长的装饰链。

四. 总结

装饰者模式在实际开发中很有用,特别是在框架开发中。我们希望框架提供的是一些稳定而方便移植的功能,那些个性化的功能可以在框架之外动态装饰上去。

未登录用户
全部评论0
到底啦