设计模式之装饰器模式

装饰器模式

装饰模式就是将其实现的功能拆分成一个个装饰器类,然后这些装饰器类实现功能并组装起来达成预期功能,还可以在不知不觉中增加其他装饰器增加新功能,而被装饰类是无感知的

1.装饰器模式的本质

装饰模式的本质 其实可以简单的理解为"给对象添加新功能的灵活方式"

(一)关键点:
  1. 动态组合:可以在运行时添加或修改对象的功能,而不需要改变对象的原始代码。
  2. 相同接口:装饰器和被装饰的对象实现相同的接口,这样可以方便地替换和使用。
  3. 组合而非继承:与其通过继承创建新的类,装饰器模式通过将一个对象包装在另一个对象中来扩展功能,这样更加灵活。
(二)举个例子:

想象一下你有一个咖啡对象,基本的咖啡只包含咖啡本身。现在你想要给咖啡添加不同的配料,比如牛奶、糖或香料。你可以创建几个装饰器:

  • 牛奶装饰器:给咖啡添加牛奶。
  • 糖装饰器:给咖啡添加糖。

每个装饰器都实现相同的接口,这样你可以将它们组合在一起。例如,你可以先用牛奶装饰器装饰咖啡,再用糖装饰器装饰牛奶咖啡,得到一杯牛奶糖咖啡,而不需要修改咖啡的原始类。

2.何时选用装饰器模式

以下情况下可以考虑使用装饰器模式:

  1. 动态添加功能:当你需要在不改变现有对象接口的前提下,动态地为对象添加额外的功能或修改其行为时,装饰器模式非常适合。
  2. 组合优于继承:如果你想通过组合的方式来扩展对象功能,避免因继承导致的类数量激增(类爆炸)或静态绑定的限制,装饰器模式是一个不错的选择。
  3. 运行时功能调整:在运行时需要灵活地添加、移除或修改对象功能,而不影响其他对象时,装饰器模式能够很好地满足这个需求。
  4. 最小化现有代码修改:当你希望在不对现有代码进行大规模修改的情况下,对现有对象进行功能扩展时,装饰器模式提供了一种优雅的解决方案。

3.装饰器的优缺点

(一)优点:
  1. 灵活性:可以随时为对象添加新功能,不用修改原来的代码。
  2. 扩展性:可以通过组合多个装饰器来增加功能,避免创建很多复杂的子类。
  3. 清晰职责:每个装饰器专注于特定的功能,使代码更易读、更易维护。
  4. 减少类数量:用装饰器来增加功能,比通过继承创建大量子类要简洁得多。
(二)缺点:
  1. 复杂性:使用多个装饰器时,代码结构可能变得复杂,理解起来比较困难。
  2. 性能问题:每添加一个装饰器,就会增加一次方法调用,可能影响性能。
  3. 调试难:调试的时候需要逐层检查,可能会比较麻烦。
  4. 可能过于复杂:如果用得太多,代码可能会变得难以理解和维护。

4、代码实现

不使用设计模式写法

1.咖啡类

java 复制代码
/**
 * 咖啡接口
 */
interface Coffee {
    /**
     * 获取描述信息
     * @return
     */
    String getDescription();

    /**
     * 获取花费
     * @return
     */
    double getCost();
}

/**
 * 黑咖啡
 */
class BlackCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "黑咖啡";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

2、测试类

java 复制代码
public class Client {

    public static void main(String[] args) {
        // 订一杯黑咖啡
        Coffee coffee = new BlackCoffee();
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());
    }
}

3.结果

tex 复制代码
订单:黑咖啡
总价:2.0

功能实现完,又想给咖啡加糖,以为直接在BlackCoffee 类中加个方法就行了,但实际上不能这么做,这么做违反了开闭原则。

那怎么实现新增功能呢,新增一个子类,让他继承BlackCoffee 即可,但如果还要加功能就还要新增子类,子类或越来越庞大。

4.给咖啡加糖

java 复制代码
/**
 * 加糖黑咖啡
 */
class SugarBlackCoffee extends BlackCoffee {
    @Override
    public String getDescription() {
        return super.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
}

5.修改测试类

java 复制代码
public class Client {

    public static void main(String[] args) {
    	// 加糖黑咖啡
        Coffee sugarBlackCoffee = new SugarBlackCoffee();
        System.out.println("订单:" + sugarBlackCoffee.getDescription());
        System.out.println("总价:" + sugarBlackCoffee.getCost());
    }
}

6.结果

tex 复制代码
订单:黑咖啡, 加糖
总价:2.3

如果想要在不继承类的情况下增加功能,一种较好的方式是增加装饰器模式

使用设计模式优化

1.定义组件接口及真实实现对象

java 复制代码
/**
 * 咖啡接口
 */
interface Coffee {
    /**
     * 获取描述信息
     * @return
     */
    String getDescription();

    /**
     * 获取花费
     * @return
     */
    double getCost();
}

/**
 * 黑咖啡
 */
class BlackCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "黑咖啡";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

2.装饰器抽象类及具体的装饰器对象

java 复制代码
/**
 * 咖啡装饰器抽象类
 */
abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription();
    }

    @Override
    public double getCost() {
        return coffee.getCost();
    }
}

/**
 * 真实加糖装饰器
 */
class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
}

/**
 * 真实加奶装饰器
 */
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 加牛奶";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
}

3.测试类

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 订一杯黑咖啡
        Coffee coffee = new BlackCoffee();
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 加牛奶
        coffee = new MilkDecorator(coffee);
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 再加糖
        coffee = new SugarDecorator(coffee);
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 加两份糖,一份奶的黑咖啡
        MilkDecorator coffee2 = new MilkDecorator(new SugarDecorator(new SugarDecorator(new BlackCoffee())));
        System.out.println("订单:" + coffee2.getDescription());
        System.out.println("总价:" + coffee2.getCost());
    }
}

4.结果

tex 复制代码
订单:黑咖啡
总价:2.0
订单:黑咖啡, 加牛奶
总价:2.5
订单:黑咖啡, 加牛奶, 加糖
总价:2.8
订单:黑咖啡, 加糖, 加糖, 加牛奶
总价:3.0999999999999996
相关推荐
天天扭码20 分钟前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒26 分钟前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
雨中rain1 小时前
贪心算法(1)
算法·贪心算法
不爱学习的YY酱1 小时前
【操作系统不挂科】<CPU调度(13)>选择题(带答案与解析)
java·linux·前端·算法·操作系统
丁总学Java1 小时前
Maven项目打包,com.sun.tools.javac.processing
java·maven
平头哥在等你1 小时前
求一个3*3矩阵对角线元素之和
c语言·算法·矩阵
飞滕人生TYF1 小时前
动态规划 详解
算法·动态规划
kikyo哎哟喂1 小时前
Java 代理模式详解
java·开发语言·代理模式
_OLi_1 小时前
力扣 LeetCode 106. 从中序与后序遍历序列构造二叉树(Day9:二叉树)
数据结构·算法·leetcode
duration~1 小时前
SpringAOP模拟实现
java·开发语言