努力保头发的打工人
努力保头发的打工人
发布于 2021-01-24 / 54 阅读 / 0 评论 / 0 点赞

装饰者模式

1 概述

定义 :动态的给一个对象添加一些额外的职责。装饰者就与生成子类来说更为灵活。通用类图如下:

装饰者模式类图.png

  • 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());
}
​
先晃一下
先晃一下
先晃一下
喝一口茶水...加了糖有点甜...加了糖有点甜...加了糖有点甜...