我所理解的设计模式系列·第7篇·从奶茶说一说装饰器模式的应用
装饰器模式的应用极为广泛,连 JDK 源码都将它奉为瑰宝,众多 IO 类就是适配器模式的最佳实践。
1. 定义
装饰器模式是一种结构型设计模式,它的定义是:“在不改变原始类结构的情况下,动态地扩展它的功能。”
2. 类图
在装饰器模式中,有这样几种角色:
- 抽象组件(Component):装饰器模式中最核心的对象,即原始对象,一般是一个接口或者是抽象类
- 具体组件(Concrete Component):抽象组件的实现类,装饰器模式装饰的目标就是它
- 装饰角色(Decorator):组合了抽象组件,同时定义一个抽象方法用来扩展抽象组件提供的能力,一般是一个抽象类
- 具体装饰角色(Concrete Decorator):实现装饰角色提供的抽象方法,是抽象组件提供能力的具体扩展实现
3. 示例
现在很多人都喜欢喝奶茶,每天一杯有益身心健康(主要是心和嘴)。当我们在喝奶茶的时候,服务员总会问要加什么料,这跟每天的终极难题——饭吃什么——是一样的,今天暂且不谈。而奶茶加料供选择的材料可能有波霸椰果布丁等,加了椰果的奶茶最后就被标为椰果奶茶售卖,加了波霸的奶茶最后就被标为波霸奶茶售卖。
今天就用奶茶店制作奶茶作为装饰器模式的例子。
首先创建一个制作奶茶的抽象类 MilkyTea,它就是装饰器模式中的抽象组件,其代码如下:
public abstract class MilkyTea {
/**
* 原料
*/
public abstract String material();
/**
* 配料
*/
public abstract String burden();
/**
* 奶茶详细描述,被装饰的方法
*/
public String description() {
return (this.burden() == null ? "" : this.burden()) + (this.material() == null ? "" : this.material());
}
}
然后创建两个具体组件,分别是椰果布丁奶茶和波霸玛奇朵奶茶,其代码如下:
public class Pudding extends MilkyTea {
@Override
public String material() {
return "布丁";
}
@Override
public String burden() {
return "椰果";
}
}
public class Macchiato extends MilkyTea {
@Override
public String material() {
return "玛奇朵";
}
@Override
public String burden() {
return "波霸";
}
}
然后创建装饰器类,也就是装饰角色,它组合了一个抽象角色,同时增强了抽象角色 MilkyTea 中的 description 方法,其代码如下:
public abstract class CustomMilkyTea {
private MilkyTea milkyTea;
public CustomMilkyTea(MilkyTea milkyTea) {
this.milkyTea = milkyTea;
}
public abstract String extra();
/**
* 扩展其功能
*
* @return
*/
public String description() {
return "自制" + this.extra() + milkyTea.description();
}
}
然后基于两个具体角色分别创建两个具体装饰角色,其代码如下:
public class CustomPudding extends CustomMilkyTea {
public CustomPudding(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
public String extra() {
return "烧仙草";
}
}
public class CustomMacchiato extends CustomMilkyTea {
public CustomMacchiato(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
public String extra() {
return "焦糖";
}
}
最后编写测试代码:
public class Test {
public static void main(String[] args) {
// 未装饰前的布丁奶茶
MilkyTea pudding = new Pudding();
System.out.println(pudding.description());
// 装饰后的布丁奶茶,需要将装饰目标传入装饰器
CustomMilkyTea customPudding = new CustomPudding(pudding);
System.out.println(customPudding.description());
// 未装饰前的玛奇朵
MilkyTea macchiato = new Macchiato();
System.out.println(macchiato.description());
// 装饰后的玛奇朵,需要将装饰目标传入装饰器
CustomMilkyTea customMacchiato = new CustomMacchiato(macchiato);
System.out.println(customMacchiato.description());
}
}
测试类输出结果如下:
椰果布丁
自制烧仙草椰果布丁
波霸玛奇朵
自制焦糖波霸玛奇朵
可以看到,装饰器类 CustomPudding 和 CustomMacchiato 在不改变 Pudding 和 Macchiato 的基础上,对原始类的方法进行了扩展(增强)。
4. 使用场景
装饰器模式的使用场景是:
- 需要快速且动态地给一个对象增加功能,同时这些功能也能动态撤销
5. 小结
本文讲述了装饰器模式,它的定义是——在不改变原始类结构的情况下,动态地扩展它的功能。
装饰器模式的优点是:
- 在不影响其他对象的情况下,动态为单个对象新增功能
- 装饰类与被装饰类相互独立,易于扩展
- 使用组合代替继承实现功能地扩展,减少继承类的存在
装饰器模式的缺点是:
- 如果存在很多层的装饰,代码结构是比较复杂的,不易理解,如 JDK 的IO类就非常复杂
6. 参考资料
- 《设计模式之禅》(第2版)
最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。
转载自:https://juejin.cn/post/7060888694193913892