设计模式之装饰器模式

装饰器模式

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

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
相关推荐
Dream_Snowar18 分钟前
速通Python 第四节——函数
开发语言·python·算法
星河梦瑾20 分钟前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富24 分钟前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想25 分钟前
JMeter 使用详解
java·jmeter
言、雲28 分钟前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
Altair澳汰尔31 分钟前
数据分析和AI丨知识图谱,AI革命中数据集成和模型构建的关键推动者
人工智能·算法·机器学习·数据分析·知识图谱
TT哇35 分钟前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
java1234_小锋1 小时前
JVM对象分配内存如何保证线程安全?
jvm
A懿轩A1 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
Python机器学习AI1 小时前
分类模型的预测概率解读:3D概率分布可视化的直观呈现
算法·机器学习·分类