1 概述
定义 :动态的给一个对象添加一些额外的职责。装饰者就与生成子类来说更为灵活。通用类图如下:
Component 一个接口或抽象类,定义最核心的对象。
ConcreateComponent 被装饰的对象。
decorator 装饰者,一般是一个抽象类,里面会有一个变量指向component
ConcreteDecorator 具体装饰类
被装饰类和装饰抽象类都继承component抽象类,装饰抽象类需要传入一个被修饰的compone子类,具体的修饰方法在修饰抽象类的实现类中实现。
2 优缺点
2.1 优点
装饰类和被装饰类可以独立发展,不会相互耦合,两者之间互相不知道具体功能。
是一种继承的一种替代方案,装饰多层,返回对象一直是component。
动态的扩展一个实现类的功能。
2.2 缺点
多层装饰比较复杂,排查问题比较麻烦,应当尽量减少装饰类的数量,一遍减少系统复杂度。
3 使用场景
需要扩展一个类的功能或给一个类增加附加功能
需要动态地给一个对象增加功能,这些功能可以动态地撤销
需要为一批兄弟类进行改装或加功能
4 代码示例
已喝水为例,装饰者用加糖加盐来表示,你不管喝的什么我都可以加东西,两者互不影响。
首先定义喝水抽象类和喝白开水,茶水两个具体被装饰的类
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 喝的饮料, 被装饰的抽象类
*/
public abstract class DrinkAbstract {
public abstract String drink();
}
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 白开水, 被装饰具体类
*/
public class PlainWater extends DrinkAbstract{
@Override
public String drink() {
// 只喝白开水没有味道
return "喝一口白开水...";
}
}
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 茶水
*/
public class Tea extends DrinkAbstract{
@Override
public String drink() {
return "喝一口茶水...";
}
}
创建修饰抽象类
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 装饰者抽象类
*/
public abstract class DecoratorAbstract extends DrinkAbstract{
// 指明装饰的对象
private DrinkAbstract drinkAbstract;
/**
* 通过构造函数传递被装饰对象
*/
public DecoratorAbstract(DrinkAbstract drinkAbstract){
this.drinkAbstract = drinkAbstract;
}
/**
* 具体的方法还是需要被装饰者执行
* @return /
*/
@Override
public String drink() {
return this.drinkAbstract.drink();
}
}
创建具体的装饰类,重写drink方法,在原有功能上进行拓展,这里是每次装饰时多执行一个加热或晃的方法,并且修改一下返回值
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 加盐的修饰类
*/
public class AddSaltDecorator extends DecoratorAbstract{
/**
* 通过构造函数传递被装饰对象
*
* @param drinkAbstract
*/
public AddSaltDecorator(DrinkAbstract drinkAbstract) {
super(drinkAbstract);
}
/**
* 重写父类的方法
* 具体的方法还是需要被装饰者执行,装饰者额外添加一些功能
*
* @return /
*/
@Override
public String drink() {
// 也可以做些其他的事情
this.doSomething();
return addSalt(super.drink());
}
/**
* 修饰方法,加盐
* @param str 未修饰的方法结果
* @return
*/
private String addSalt(String str){
StringBuffer sb = new StringBuffer(str);
sb.append("加了盐有点咸...");
return sb.toString();
}
private void doSomething(){
System.out.println("先加热一下");
}
}
/**
* @author chiangtaol
* @date 2020-12-04
* @describe 加糖的装饰类
*/
public class AddSugarDecorator extends DecoratorAbstract{
/**
* 通过构造函数传递被装饰对象
*
* @param drinkAbstract
*/
public AddSugarDecorator(DrinkAbstract drinkAbstract) {
super(drinkAbstract);
}
/**
* 重写父类的方法
* 具体的方法还是需要被装饰者执行,装饰者额外添加一些功能
*
* @return /
*/
@Override
public String drink() {
// 也可以做些其他的事情
this.doSomething();
return addSugar(super.drink());
}
/**
* 修饰方法,加糖
* @param str 未修饰的方法结果
* @return
*/
private String addSugar(String str){
StringBuffer sb = new StringBuffer(str);
sb.append("加了糖有点甜...");
return sb.toString();
}
private void doSomething(){
System.out.println("先晃一下");
}
}
下面是测试实例
@Test
void drink() {
// 首先不适用装饰
// 创建白开水,喝一口
DrinkAbstract planWater = new PlainWater();
System.out.println(planWater.drink());
// 创建茶水,喝一口
DrinkAbstract tea = new Tea();
System.out.println(tea.drink());
System.out.println("------------------------------");
// 使用装饰者
// 加糖
DecoratorAbstract decorator = new AddSugarDecorator(planWater);
System.out.println(decorator.drink());
// 加盐
DecoratorAbstract decoratorAbstract = new AddSaltDecorator(tea);
System.out.println(decoratorAbstract.drink());
System.out.println("------------------------------");
}
喝一口白开水...
喝一口茶水...
------------------------------
先晃一下
喝一口白开水...加了糖有点甜...
先加热一下
喝一口茶水...加了盐有点咸...
------------------------------
还可以在原来的基础上多次装饰,我就喜欢糖,就多加几次,测试代码如下:
@Test
public void test(){
// 创建茶水
DrinkAbstract tea = new Tea();
//加糖
DecoratorAbstract decoratorAbstract = new AddSugarDecorator(tea);
//加糖
decoratorAbstract = new AddSugarDecorator(decoratorAbstract);
//加糖
decoratorAbstract = new AddSugarDecorator(decoratorAbstract);
System.out.println(decoratorAbstract.drink());
}
先晃一下
先晃一下
先晃一下
喝一口茶水...加了糖有点甜...加了糖有点甜...加了糖有点甜...