Decorator(装饰者)模式
Decorator 是一种结构设计模式,旨在促进代码复用。于 Mixin 相类似,它们可以被认为是另一可行对象子类化的替代方案。
通常,Decorator 提供了将行为动态添加至系统的现有类的能力。
装饰者可以用于修改现有的系统,希望在系统中为对象添加额外的功能,而不需要大量修改实用它们的底层代码。
Decorator 模式并不严重依赖于创建对象的方式,而是关注扩展其额外功能。我们实用了一个单一的基本对象并逐步添加提供额外功能的 decorator 对象,而不是仅仅依赖原型继承。这个想法是:向基本对象添加(装饰)属性或方法,而不是进行子类化,因此它较为精简。
1 | // 车辆vehicle构造函数 |
这种类型的简单实现是可行的,但它并不能真正证明装饰者所提供的所有又是。为此,首先要查以下改编的咖啡示例,该示例来自 Freeman、Sierra 和 Bates 所著的一本名《深入浅出设计模式》书籍,它围绕的是模拟购买苹果笔记本。
1 | // 被装饰的对象构造函数 |
在这个示例中,Decorator 重写 MacBoook()超类对象的 cost()函数返回 MacBook 的当前价格加上特定的升级价格。
我们认为装饰作为并没有重写原始 Macbook 对象的构造函数方法(如 screenSize()),为 Mackbook 定义的其他属性也一样,依然保持不变完好无损。
伪经典 Decorator(装饰者)
接口
《JavaScript 设计模式》(PJDP)将 Decorator 模式描述为一种用于在相同接口的其他对象内部透明地包装对象的模式。接口应该是对象定义方法的一种方式,但是,它实际上并不直接指定如何实现这些方法。
下面是使用鸭子类型在 Javascript 中实现接口的一个示例,这种方法帮助确定一个对象是否基于实现方法的构造函数/对象的实例。
1 | //定义一个静态方法来实现接口与实现类的直接检验 |
接口的最大问题是,在 Javascript 中没有为它们提供内置支持,试图模仿可能不太合适的另外一中语言特点是有风险的。可以在不花费大量性能成本的情况下使用享元接口,下面继续看一下使用相同概念的抽象装饰者。
抽象 Decorator(抽象装饰者)
为了演示改版本的 Decorator 模式的结构,假设我们有一个超类,再次模拟 Macbook,以及模拟一个商店允许我们“装饰”苹果笔记本并收取增强功能的额外费用。
增强功能可以包括将内存升级到 8GB、雕刻、Parallels 或外壳。如果为每个增强选项组合使用单个子类来模拟它,看起来可能就是这样的:
1 | var Macbook = function() { |
这是一个不切实际的方案,这里我们试着使用装饰者来更好解决这个问题。
我们只需要创建五个新的装饰者类,而不是需要之前看到的所有组合。在这些增强类上调用的方法将被传递给 Macbook 类。
1 | var Macbook = new Interface("Macbook",[ |
使用jQuery的装饰者
与我们涉及到的其他模式一样,也有一些使用jQuery实现的装饰者模式的示例。jQuery.extend()允许我们在运行时或者在随后一个点上动态地将两个或两个以上的对象(和它们的属性)一起扩展(或合并)为一个单一对象。
在这种情况下,一个目标对象可以用新功能来装饰,而不会在源/超类对象中破坏或重写现有的方法
接下来的示例有三个对象:defaults、options和settings。该任务的目的是为了装饰defaults对象,将options中的额外功能附加到defaults上。我们必须首先使defaults保持未接触状态,并且保持稍后可以访问其属性或函数的能力,然后,给defaults赋予使用装饰属性和函数的能力,这些装饰属性和函数是从options里获取来的;
1 | var decoratorApp = decoratorApp || {}; |
优点和缺点
对象可以被新行为包装或“装饰”,然后可以被继续使用,而不必担心被修改的基本对象。在一个更广泛的上下文中,这种模式也使我们不必依靠大量的子类来获得同样的好处。
如果管理不当,它会极大的复杂化应用程序架构,因为它向我们的命名空间引入了很多小型但类似的对象。