概述
- 装饰器模式(Decorator Design Pattern,也叫包装设计模式,属于结构型模式,它是作为现有的类的一个包装,允许向一个现有的对象添加新的功能,同时又不改变其结构
- 给对象增加功能,一般两种方式
继承
或关联
组合,将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为来增强功能,这个就是装饰器模式,比继承模式更加灵活
核心思想
-
抽象组件
(Component):定义了一个接口,用于规范对象的基本行为,它是装饰器和具体组件都需要实现的接口或基类 -
具体组件
(Concrete Component)- Component的具体实现,也就是我们要装饰的具体对象
- 实现了核心角色的具体对象
-
装饰者组件
(Decorator)- 定义具体装饰者的行为规范, 和
Component
角色有相同的接口,持有组件(Component
)对象的实例引用 - 主要工作就是在调用组件原有的方法之前或之后,附加额外的操作
- 定义具体装饰者的行为规范, 和
-
具体装饰物
(Concrete Decorator):- 是装饰器类的实例,负责给组件添加新的行为或属性
- 每次只有一个具体的装饰器在工作,但可以通过组合多个装饰器,一层层地包装具体组件,形成装饰链,从而实现功能的叠加
场景使用
- GUI编程
- 在图形用户界面编程中,装饰器模式可以用来动态地给按钮、窗口等组件添加边框、背景色、字体样式等功能。例如,可以创建一个基本按钮类,然后通过装饰器为其添加滚动条、鼠标悬浮效果、点击反馈等装饰
- 文件系统操作
- 对文件流(如InputStream, OutputStream)进行功能增强时,可以使用装饰器模式添加压缩、加密、缓冲等功能,如ZipInputStream、BufferedInputStream等
- 网络请求处理
- HTTP客户端库可能会使用装饰器模式来添加重试机制、超时控制、错误处理、日志输出等功能,每次请求都可以通过添加不同的装饰器来满足特定的需求
- 餐饮行业点餐系统
- 咖啡、饮品或快餐菜单中,基础产品(如原味咖啡)可通过装饰器模式添加额外的调料或配料(如糖、奶油、巧克力酱等),构建出复杂的产品组合
- 游戏开发
- 游戏角色能力的扩展常常使用装饰器模式,如为角色添加临时或永久的攻击提升、防御增强、速度增加等特性
- 日志框架
- 日志系统中,可以利用装饰器模式为原始的日志输出添加不同级别的过滤器、格式化器、远程传输器等装饰器,按需组合
优缺点
优点
- 装饰模式与继承关系的目的都是要扩展对象的功能,但装饰模式可以提供比继承更多的灵活性
- 使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,原有代码无须改变,符合
开闭原则
缺点
- 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂 (多层包装)
- 增加系统的复杂度,加大学习与理解的难度
装饰器模式
和桥接模式
对比
- 相同点都是通过封装其他对象达到设计的目的,和对象适配器也类似,有时也叫半装饰设计模式
- 没有装饰者和被装饰者的主次区别,桥接和被桥接者是平等的,桥接可以互换,不用继承自同一个父类
- 桥接模式不用使用同一个接口;装饰模式用同一个接口装饰,接口在父类中定义
示例
场景应用
- 背景需求
- 小帅由于公司发了项目奖金,不够买跑车,就先买自行车,店家里面有小号、中号、大号等规格的自行车,然后改造加一个喇叭,后来不够又要加多一个,一个防爆胎不够,又有两个,存在很多个随机组合的改装,店家就苦恼了,这样的结构难搞,价格也难算,而且需求再变动,就更麻烦了。使用装饰者就可以解决这个问题
抽象组件(Bike)
java
public interface Bike {
String getDescription();
Integer getPrice();
}
具体组件(BigBike、SmallBike)
java
public class SmallBike implements Bike{
private String description = "小号自行车";
@Override
public String getDescription() {
return description;
}
/**
* 100元是小号自行车的价格
* @return
*/
@Override
public Integer getPrice() {
return 200;
}
}
java
public class BigBike implements Bike{
private String description = "大号自行车";
@Override
public String getDescription() {
return description;
}
/**
* 200元是大号自行车的价格
* @return
*/
@Override
public Integer getPrice() {
return 200;
}
}
装饰者组件(BikeDecorator)
java
public class BikeDecorator implements Bike{
private String description = "我只是装饰器,啥都不表示,子类帮我传递";
@Override
public String getDescription() {
return description;
}
@Override
public Integer getPrice() {
return 0;
}
}
具体装饰物(RSCBikeDecorator,SuonaBikeDecorator)
java
public class RSCBikeDecorator extends BikeDecorator{
private String description = "增加一个防爆胎";
private Bike bike;
public RSCBikeDecorator(Bike bike){
this.bike = bike;
}
@Override
public String getDescription() {
return bike.getDescription() + ","+ description;
}
/**
* 100 是防爆胎的价格
* @return
*/
@Override
public Integer getPrice() {
return bike.getPrice() + 100;
}
}
java
public class SuonaBikeDecorator extends BikeDecorator{
private String description = "增加一个喇叭";
private Bike bike;
public SuonaBikeDecorator(Bike bike){
this.bike = bike;
}
@Override
public String getDescription() {
return bike.getDescription() + ","+ description;
}
/**
* 50 是唢呐喇叭的价格
* @return
*/
@Override
public Integer getPrice() {
return bike.getPrice() + 50;
}
}
测试
小号自行车
java
@Test
public void decoratorTest(){
Bike bike = new SmallBike();
System.out.println("description:" + bike.getDescription()+" price:"+bike.getPrice());
BikeDecorator rscBikeDecorator = new RSCBikeDecorator(bike);
System.out.println("description:" + rscBikeDecorator.getDescription()+" price:"+rscBikeDecorator.getPrice());
BikeDecorator suonaBikeDecorator = new SuonaBikeDecorator(rscBikeDecorator);
System.out.println("description:" + suonaBikeDecorator.getDescription()+" price:"+suonaBikeDecorator.getPrice());
}
大号自行车
java
@Test
public void decoratorTest(){
Bike bike = new BigBike();
System.out.println("description:" + bike.getDescription()+" price:"+bike.getPrice());
BikeDecorator rscBikeDecorator = new RSCBikeDecorator(bike);
System.out.println("description:" + rscBikeDecorator.getDescription()+" price:"+rscBikeDecorator.getPrice());
BikeDecorator suonaBikeDecorator = new SuonaBikeDecorator(rscBikeDecorator);
System.out.println("description:" + suonaBikeDecorator.getDescription()+" price:"+suonaBikeDecorator.getPrice());
}