装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

使用场景

  1. 动态地添加功能 :当你需要在运行时动态地向对象添加新的功能或修改现有功能时,装饰模式非常有用。它允许你在不修改现有代码的情况下,通过添加装饰器来实现功能的扩展。
  2. 避免类爆炸 :使用装饰模式可以避免类的爆炸式增长。当有多种组合方式和功能变体时,通过装饰模式可以将这些功能分解成单独的装饰器,从而减少类的数量和复杂度。
  3. 避免静态继承的局限性 :装饰模式可以避免静态继承所带来的限制和问题。通过组合而不是继承,可以更灵活地组合和调整功能,使得系统更易于扩展和维护。
  4. 开放封闭原则 :装饰模式符合开放封闭原则,即对扩展开放,对修改封闭。通过装饰模式,可以在不修改现有代码的情况下,向系统添加新的功能,从而提高系统的灵活性和可维护性。
  5. 适应变化需求 :当需求经常变化或者功能组合具有较高的复杂性时,装饰模式可以帮助你应对变化的需求,使得系统更易于适应新的功能和变化。

代码实现

假设我们有一个咖啡店,我们可以为咖啡添加各种配料,如牛奶、糖浆等。我们将使用装饰模式来动态地添加这些配料,而不是在每个咖啡类中定义所有可能的组合。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import "fmt"

// Coffee 是咖啡接口
type Coffee interface {
GetDescription() string
Cost() float64
}

// BaseCoffee 是基础咖啡类
type BaseCoffee struct{}

func (c *BaseCoffee) GetDescription() string {
return "Base Coffee"
}

func (c *BaseCoffee) Cost() float64 {
return 2.0
}

// MilkDecorator 是牛奶装饰器
type MilkDecorator struct {
coffee Coffee
}

func (d *MilkDecorator) GetDescription() string {
return d.coffee.GetDescription() + ", Milk"
}

func (d *MilkDecorator) Cost() float64 {
return d.coffee.Cost() + 0.5
}

// SyrupDecorator 是糖浆装饰器
type SyrupDecorator struct {
coffee Coffee
}

func (d *SyrupDecorator) GetDescription() string {
return d.coffee.GetDescription() + ", Syrup"
}

func (d *SyrupDecorator) Cost() float64 {
return d.coffee.Cost() + 0.3
}

func main() {
// 创建基础咖啡
baseCoffee := &BaseCoffee{}

// 添加牛奶装饰器
milkCoffee := &MilkDecorator{coffee: baseCoffee}
fmt.Println("Description:", milkCoffee.GetDescription())
fmt.Println("Cost:", milkCoffee.Cost())

// 添加糖浆装饰器
syrupMilkCoffee := &SyrupDecorator{coffee: milkCoffee}
fmt.Println("Description:", syrupMilkCoffee.GetDescription())
fmt.Println("Cost:", syrupMilkCoffee.Cost())
}

结语

装饰模式可以帮助我们动态地向对象添加新的功能,而不需要修改现有的代码。通过装饰模式,我们可以将对象的功能进行组合,从而实现功能的扩展和组合,而不会导致类的爆炸式增长。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。