设计模式之结构型设计模式-装饰者模式

1. 模式简介

在不改变原有对象的基础之上,将新功能附加到原有对象上,提供了比继承更有弹性的解决方案(扩展原有对象的功能),属于结构型设计模式

2. 示例代码

这回咱们就土豪一把,模拟购买 2019 款 MacBook Pro 16 寸,来演示装饰者模式的逻辑。

  • MacBook Pro 的基础信息
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
/**
* <p>
* 苹果笔记本
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 16:51
*/
public interface MacBookPro {
/**
* 套餐名称
*
* @return 套餐名称
*/
String getComboName();

/**
* 硬盘
*
* @return 硬盘
*/
String getHardDisk();

/**
* 内存
*
* @return 内存
*/
String getMemory();

/**
* 价格
*
* @return 价格
*/
Double getPrice();
}
  • 定义我们的套餐装饰器
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
/**
* <p>
* 套餐 - 装饰器
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 17:08
*/
@AllArgsConstructor
public abstract class ComboDecorator implements MacBookPro {
private MacBookPro macBookPro;

/**
* 套餐名称
*
* @return 套餐名称
*/
@Override
public String getComboName() {
return this.macBookPro.getComboName();
}

/**
* 硬盘
*
* @return 硬盘
*/
@Override
public String getHardDisk() {
return this.macBookPro.getHardDisk();
}

/**
* 内存
*
* @return 内存
*/
@Override
public String getMemory() {
return this.macBookPro.getMemory();
}

/**
* 价格
*
* @return 价格
*/
@Override
public Double getPrice() {
return this.macBookPro.getPrice();
}
}
  • 基础套餐
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
/**
* <p>
* 苹果笔记本基础套餐
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 16:52
*/
public class BaseMacBookProCombo implements MacBookPro {

/**
* 套餐名称
*
* @return 套餐名称
*/
@Override
public String getComboName() {
return "基础套餐";
}

/**
* 硬盘
*
* @return 硬盘
*/
@Override
public String getHardDisk() {
return "1T 固态硬盘";
}

/**
* 内存
*
* @return 内存
*/
@Override
public String getMemory() {
return "16GB 2666MHz DDR4 内存";
}

/**
* 价格
*
* @return 价格
*/
@Override
public Double getPrice() {
return 22199d;
}
}
  • 16G内存套餐
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
/**
* <p>
* 内存套餐
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 17:17
*/
public class MemoryCombo extends ComboDecorator {
public MemoryCombo(MacBookPro macBookPro) {
super(macBookPro);
}

/**
* 套餐名称
*
* @return 套餐名称
*/
@Override
public String getComboName() {
return super.getComboName() + " + 升级内存";
}

/**
* 内存
*
* @return 内存
*/
@Override
public String getMemory() {
return super.getMemory() + " + 16G 内存";
}

/**
* 价格
*
* @return 价格
*/
@Override
public Double getPrice() {
return super.getPrice() + 2936;
}
}
  • 1T硬盘套餐
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
/**
* <p>
* 硬盘套餐
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 17:15
*/
public class HardDiskCombo extends ComboDecorator {
public HardDiskCombo(MacBookPro macBookPro) {
super(macBookPro);
}

/**
* 套餐名称
*
* @return 套餐名称
*/
@Override
public String getComboName() {
return super.getComboName() + " + 升级硬盘";
}

/**
* 硬盘
*
* @return 硬盘
*/
@Override
public String getHardDisk() {
return super.getHardDisk() + " + 1TB 固态硬盘";
}

/**
* 价格
*
* @return 价格
*/
@Override
public Double getPrice() {
return super.getPrice() + 2936;
}
}
  • 测试类,我们需要购买一台 2T 固态硬盘 64G 内存的 MacBook Pro
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
/**
* <p>
* 装饰者模式 - 测试类
* </p>
*
* @author yangkai.shen
* @date Created in 2019/12/13 17:19
*/
public class PatternTest {
public static void main(String[] args) {
// MacBookPro 基础套餐
MacBookPro macBookPro = new BaseMacBookProCombo();
printInfo(macBookPro);

// 升级内存 16G -> 64G
macBookPro = new MemoryCombo(macBookPro);
macBookPro = new MemoryCombo(macBookPro);
macBookPro = new MemoryCombo(macBookPro);
printInfo(macBookPro);

// 升级硬盘 1T -> 2T
macBookPro = new HardDiskCombo(macBookPro);
printInfo(macBookPro);
}

private static void printInfo(MacBookPro macBookPro) {
System.out.println("当前套餐: " + macBookPro.getComboName());
System.out.println("内存: " + macBookPro.getMemory());
System.out.println("硬盘: " + macBookPro.getHardDisk());
System.out.println("总价为: " + macBookPro.getPrice() + " 元");
System.out.println("\n======================================\n");
}
}
  • 测试结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当前套餐: 基础套餐
内存: 16GB 2666MHz DDR4 内存
硬盘: 1T 固态硬盘
总价为: 22199.0 元

======================================

当前套餐: 基础套餐 + 升级内存 + 升级内存 + 升级内存
内存: 16GB 2666MHz DDR4 内存 + 16G 内存 + 16G 内存 + 16G 内存
硬盘: 1T 固态硬盘
总价为: 31007.0 元

======================================

当前套餐: 基础套餐 + 升级内存 + 升级内存 + 升级内存 + 升级硬盘
内存: 16GB 2666MHz DDR4 内存 + 16G 内存 + 16G 内存 + 16G 内存
硬盘: 1T 固态硬盘 + 1TB 固态硬盘
总价为: 33943.0 元

======================================

3. UML 图例

design-pattern-observer-uml

4. 应用

1
2
3
4
5
6
// JDK 中的 IO 类
// BufferedReader、InputStream、OutputStream

// Spring 中处理事务缓存 TransactionAwareCacheDecorator

// Spring MVC 中对 Header 的处理 HttpHeadResponseDecorator

5. 场景

  • 用于扩展一个类的功能。
  • 给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

6. 优缺点

优点: 1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。 2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。 3、装饰者完全遵守开闭原则。

缺点: 1、会出现更多的代码,更多的类,增加程序复杂性。 2、动态装饰时,多层装饰时会更复杂。

7. 完整代码地址

https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/decorator

-------------本文结束  感谢您的阅读-------------
xkcoding wechat
欢迎来我的公众号「xkcoding小凯扣丁」逛逛
o(╯□╰)o 赞助一杯咖啡 ~~