概念

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许动态地给一个对象添加一些额外的职责,而不需要通过继承来实现

装饰器模式通过引入一个装饰器类和具体组件类来实现这一点


在装饰器模式中,通常存在四个核心角色:抽象组件(Component)、具体组件(Concrete Component)、抽象装饰器(Decorator)和具体装饰器(Concrete Decorator)

抽象组件定义了对象的基本行为,具体组件实现了抽象组件的行为,抽象装饰器继承了抽象组件,并持有一个抽象组件的引用,具体装饰器继承了抽象装饰器,并通过调用抽象组件的方法来增加额外的职责


装饰器模式的核心思想是通过组合的方式来动态地给一个对象添加一些额外的职责,而不需要通过继承来修改原有的类结构

这种模式的优点在于,可以在不改变原有对象的情况下,动态地增加新的功能,从而提高了系统的灵活性和可维护性

装饰器模式适用于以下情况:

  1. 需要动态地给一个对象添加一些额外的职责,而不希望通过继承来实现
  2. 需要在不影响其他对象的情况下,给一个对象添加一些额外的功能


举个简单的例子,考虑一个咖啡店

咖啡店提供基本的咖啡(具体组件),但是顾客可能需要在咖啡中加入额外的调料,比如牛奶、糖等

使用装饰器模式,可以通过装饰器来动态地给咖啡对象添加额外的调料功能,而不需要修改原有的咖啡类结构,从而实现了对咖啡对象的灵活扩展


实现条件

要实现装饰器模式,通常需要以下条件:

  1. 组件接口

    一个定义了对象的基本功能的接口

  2. 具体组件

    实现或继承自组件接口的类,它定义了基本功能

  3. 装饰器类

    实现或继承自组件接口的类,它包含具体组件的实例,并添加新的功能


优点

  1. 增加职责

    可以在不修改原始代码的情况下,向对象添加新的功能

  2. 灵活性

    可以动态地添加或删除功能

  3. 功能组合

    可以组合多个装饰器,创造出复杂的功能


缺点

  1. 复杂性

    使用大量装饰器会增加系统的复杂性

  2. 维护难度

    过多的层次和装饰可能会使系统变得难以维护

  3. 性能问题

    在某些情况下,过多的包装层可能会影响性能


实现方式

基本对象定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 定义接口(或者抽象类)
class CoffeeInterface {
getCost() {
throw new Error("Method 'getCost()' must be implemented.")
}

getDescription() {
throw new Error("Method 'getDescription()' must be implemented.")
}
}

// 基本咖啡类
export class SimpleCoffee extends CoffeeInterface {
getCost() {
return 5
}

getDescription() {
return "Simple Coffee"
}
}

// 抽象装饰器类
export class CoffeeDecorator extends CoffeeInterface {
constructor(coffee) {
super()
if (!(coffee instanceof CoffeeInterface)) {
throw new Error("Decorator requires a CoffeeInterface instance.")
}
this.coffee = coffee
}
}

在基本对象上增加新东西的装饰器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 牛奶装饰器
class MilkDecorator extends CoffeeDecorator {
getCost() {
return this.coffee.getCost() + 1
}

getDescription() {
return `${this.coffee.getDescription()}, Milk`
}
}

// 糖装饰器
class SugarDecorator extends CoffeeDecorator {
getCost() {
return this.coffee.getCost() + 0.5
}

getDescription() {
return `${this.coffee.getDescription()}, Sugar`
}
}

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
// 使用装饰器
let myCoffee = new SimpleCoffee() // 创建一个基本的咖啡对象
console.log(myCoffee.getDescription()) // 输出:Simple Coffee
console.log(myCoffee.getCost()) // 输出:5

myCoffee = new MilkDecorator(myCoffee) // 向咖啡中添加牛奶
console.log(myCoffee.getDescription()) // 输出:Simple Coffee, Milk
console.log(myCoffee.getCost()) // 输出:6

myCoffee = new SugarDecorator(myCoffee) // 向咖啡中添加糖
console.log(myCoffee.getDescription()) // 输出:Simple Coffee, Milk, Sugar
console.log(myCoffee.getCost()) // 输出:6.5
  1. 接口定义

    CoffeeInterface 是一个抽象接口或类,定义了 getCost()getDescription() 两个方法

    这些方法代表了咖啡对象的基本功能,装饰器和基本对象都应该实现这个接口

  2. 基本对象

    SimpleCoffee 类是一个基本的咖啡对象,继承自 CoffeeInterface,并实现了 getCost()getDescription() 方法

    这个类代表了最基本的咖啡,没有任何额外的添加

  3. 抽象装饰器类

    CoffeeDecorator 是一个抽象装饰器类,继承自 CoffeeInterface

    它的构造函数接受一个 CoffeeInterface 类型的对象,并确保传入的对象符合该接口

  4. 具体装饰器类

    MilkDecoratorSugarDecorator 类继承自 CoffeeDecorator,分别代表牛奶和糖装饰器

    它们在原始对象的基础上扩展了 getCost()getDescription() 方法,添加了相应的功能

  5. 组合装饰器

    装饰器模式允许将不同的装饰器组合在一起

    通过在装饰器链中逐一应用装饰器,myCoffee 对象依次被 MilkDecoratorSugarDecorator 装饰,从而在原始 SimpleCoffee 对象上添加了牛奶和糖的功能


场景

  1. React 高阶组件

    • 高阶组件(HOC,Higher-Order Component)是 React 中的一种模式,它类似于装饰器模式。高阶组件接受一个组件作为参数,并返回一个新的组件。这种方式可以用来增强组件的功能
    • 例如,react-redux 中的 connect 函数就是一个高阶组件,它将组件与 Redux 的状态和派发函数相连接
  2. Node.js 中的中间件

    • 在 Node.js 中,特别是在 Web 框架(如 Express 和 Koa)中,中间件模式类似于装饰器模式。中间件通过将请求和响应对象传递给一系列函数,从而动态地添加或修改功能。
    • 例如,在 Express 中,你可以使用中间件来对请求进行身份验证、解析数据、添加日志记录等
  3. Python 装饰器

    • 虽然 Python 不是 JavaScript,但其装饰器特性可以提供一个参考

      Python 中的装饰器用于为函数或方法添加额外的行为

      在 JavaScript 中,你可以通过闭包和高阶函数来模拟类似的效果

  4. Angular 装饰器

    • Angular 框架使用装饰器

      (如 @Component@Injectable 等)来定义组件、服务、模块等

      这些装饰器本质上是函数,用于为类添加元数据和功能

  5. JavaScript 中的工具和库

    • Lodashunderscore 等工具库提供了一些高阶函数

      可以用于以装饰器的形式对函数进行封装,如 _.debounce_.throttle

    • TypeScript 装饰器

      TypeScript 支持类和方法装饰器

      它们可以用来为类或方法添加元数据或功能

      虽然这在语法上不同于传统的装饰器模式,但它提供了类似的功能


源代码