设计模式-10-装饰模式

装饰模式是一种结构型设计模式, 允许在运行时动态地给对象添加新的行为。

更改一个对象的行为时,不能忽视继承可能引发的几个严重问题

  • 继承是静态的,无法在运行时更改已有对象的行为, 只能使用由不同子类创建的对象替代当前的整个对象。
  • 子类只能有一个父类。 大部分编程语言不允许一个类同时继承多个类的行为

业务逻辑

装饰模式通过将对象放入包装器中来实现这一点,每个包装器都提供了额外的功能。在装饰模式中,有四个角色:

  • Component(抽象构件):定义了一个对象接口,可以给这些对象动态地添加职责。
  • ConcreteComponent(具体构件):定义了一个具体的对象,也可以给这个对象添加一些职责。
  • Decorator(装饰者):持有一个抽象构件的引用,并定义了一个与抽象构件接口一致的接口。
  • ConcreteDecorator(具体装饰者):负责给具体构件添加额外的职责
Decorator

代码实现

代码路径:https://github.com/XBoom/DesignPatterns/tree/main/09_Decorator

  1. 首先有一个具体的组件ConcreteComponent构建(这里为具体电池组件 凝聚态电池)

    1
    2
    3
    4
    5
    type BatteryComponent struct{}

    func (c *BatteryComponent) Name() string {
    return "Condensed Battery"
    }
  2. Component装饰构建类似封装一层,在ConcreteComponent的基础上进一步拓展,那么就声明一个抽象组件(这里指电池)

    1
    2
    3
    4
    // Battery 抽象构件,给这个对象动态地添加职责
    type Battery interface {
    Name() string
    }
  3. 装饰者Decorator则是在前面组件ConcreteComponent的基础上拓展(这里举例汽车)

    1
    2
    3
    4
    // Car 装饰者
    type Car interface {
    Run() string
    }
  4. 这里构建2 个具体装饰者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // BYD 具体装饰者 BYD
    type BYD struct {
    battery Battery
    }

    func (d *BYD) Run() string {
    return "BYD use " + d.battery.Name() + " run"
    }

    // Tesla 具体装饰者 Tesla
    type Tesla struct {
    battery Battery
    }

    func (t *Tesla) Run() string {
    return "Tesla use " + t.battery.Name() + " run"
    }

单元测试

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
var ExpectBattery = "Condensed Battery"
var ExpectBYD = "BYD use Condensed Battery run"
var ExpectTesla = "Tesla use Condensed Battery run"

func TestBYD_Run(t *testing.T) {
//1. 构建电池组件
battery := new(BatteryComponent)
res := battery.Name()
if strings.Compare(res, ExpectBattery) != 0 {
t.Fatalf("Eoncrete fail expect %s acture %s", ExpectBattery, res)
}

//2. 汽车装饰电池
byd := &BYD{battery}
res = byd.Run()
if strings.Compare(res, ExpectBYD) != 0 {
t.Fatalf("Eoncrete fail expect %s acture %s", ExpectBYD, res)
}
}

func TestTesla_Run(t *testing.T) {
//1. 构建电池组件
battery := new(BatteryComponent)
res := battery.Name()
if strings.Compare(res, ExpectBattery) != 0 {
t.Fatalf("Eoncrete fail expect %s acture %s", ExpectBattery, res)
}

//2. 汽车装饰电池
tesla := &Tesla{battery}
res = tesla.Run()
if strings.Compare(res, ExpectTesla) != 0 {
t.Fatalf("Eoncrete fail expect %s acture %s", ExpectTesla, res)
}
}

适用场景

  1. 如果希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为,可以使用装饰模式。
  2. 如果用继承来扩展对象行为的方案难以实现或者根本不可行,你可以使用该模式

总结

  • 无需创建新子类即可扩展对象的行为。
  • 可以在运行时添加或删除对象的功能。
  • 可以用多个装饰封装对象来组合几种行为。
  • 单一职责原则。 可以将实现了许多不同行为的一个大类拆分为多个较小的类

参考链接

  1. https://refactoringguru.cn/design-patterns/decorator
  2. https://lailin.xyz/post/decorator.html