我所理解的设计模式系列·第7篇·从奶茶说一说装饰器模式的应用

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战

装饰器模式的应用极为广泛,连 JDK 源码都将它奉为瑰宝,众多 IO 类就是适配器模式的最佳实践。

design-patttern-7-装饰器模式-封面

1. 定义

装饰器模式是一种结构型设计模式,它的定义是:“在不改变原始类结构的情况下,动态地扩展它的功能。”

2. 类图

在装饰器模式中,有这样几种角色:

  • 抽象组件(Component):装饰器模式中最核心的对象,即原始对象,一般是一个接口或者是抽象类
  • 具体组件(Concrete Component):抽象组件的实现类,装饰器模式装饰的目标就是它
  • 装饰角色(Decorator):组合了抽象组件,同时定义一个抽象方法用来扩展抽象组件提供的能力,一般是一个抽象类
  • 具体装饰角色(Concrete Decorator):实现装饰角色提供的抽象方法,是抽象组件提供能力的具体扩展实现

design-patttern-7-装饰器模式-1

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版)

最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。

Guess you like

Origin juejin.im/post/7060888694193913892