设计模式之装饰器模式

装饰器模式

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

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
相关推荐
xiaohaiAIgeo8 分钟前
【2026年】ASHRAE 110与EN 14175通风柜测试标准对比:进口与国产品牌性能差距
java·前端·数据库·科普知识
测试_AI_一辰27 分钟前
AI Agent 评测最隐蔽的坑-记忆
人工智能·算法·ai·自动化·ai编程
lucas_AI36 分钟前
给YOLO检测器插 LoRA,'插对地方'比'插什么'更要命
人工智能·算法
AI人工智能+电脑小能手1 小时前
大白话说Java设计模式-08-建造者模式(业务实战篇)
java·设计模式·建造者模式·架构设计·对象构建
LuminousCPP1 小时前
数据结构-时间与复杂度|时间/空间复杂度 + 两道力扣练习 + 二分查找复盘
c语言·数据结构·经验分享·算法·leetcode
Nil2081 小时前
leetcode 三数之和
数据结构·算法·leetcode
xbgRS1 小时前
java中的线程
java
小小龙学IT1 小时前
C++ std::vector 底层实现深度解析:内存布局、扩容策略、移动语义与迭代器失效
开发语言·c++·算法
eBest数字化转型方案2 小时前
从工程视角拆解冰柜资产管理:拍照识别 pipeline、纯净度算法与 IoT 选型踩坑
人工智能·物联网·算法
Interview Aid1122 小时前
Roblox OA 面经|小游戏+编程双修,Coding 两题满分通过
算法