Bootstrap

(八)趣学设计模式 之 装饰器模式!

在这里插入图片描述


🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解适配器模式请看: (七)趣学设计模式 之 适配器模式!

这篇文章带你详细认识一下设计模式中的装饰器模式

一、 啥是装饰器模式?

想象一下,你点了一杯咖啡 ☕️,觉得味道有点单调,想加点料,比如加一份牛奶 🥛,或者加一份糖 🍬,甚至加一份巧克力酱 🍫。 每次加料,咖啡的味道都会变得不一样 😋。

装饰器模式,就是用来动态地给一个对象添加一些额外的职责! 它可以让你在不修改原有对象的基础上,扩展对象的功能 ➕。

简单来说,就是给对象穿上不同的“衣服”,让它拥有不同的功能! 🧥👔

  • 你想给一个对象添加一些额外的功能,但是不想修改它的代码: 就像你想给咖啡加料,但是不想修改咖啡的制作方法 ☕️!
  • 你想动态地给对象添加功能,而不是静态地继承: 就像你想根据自己的喜好,随时给咖啡加不同的料 🥛🍬🍫!
  • 你想避免创建大量的子类: 就像你不想为每种加料的咖啡都创建一个新的类 ☕️+🥛, ☕️+🍬, ☕️+🍫!

二、 为什么要用装饰器模式?

用装饰器模式,好处多多 👍:

  • 扩展性好: 可以动态地添加新的装饰器,扩展对象的功能 ➕!
  • 灵活性高: 可以灵活地组合不同的装饰器,实现不同的功能组合 🤸!
  • 符合开闭原则: 可以在不修改原有代码的情况下,增加新的装饰器,扩展功能 🆕!
  • 避免了继承带来的类爆炸问题: 不需要创建大量的子类,减少了类的数量 💥!

三、 装饰器模式的实现方式

装饰器模式主要包含以下几个角色:

  • Component(组件): 定义一个对象接口,可以给这些对象动态地添加职责。 ☕️ (比如:咖啡)
  • ConcreteComponent(具体组件): 定义一个具体的对象,实现了组件接口。 ☕️ (比如:原味咖啡)
  • Decorator(装饰器): 包含一个指向组件对象的引用,并定义一个与组件接口一致的接口。 🧥 (比如:调味品)
  • ConcreteDecorator(具体装饰器): 具体的装饰器类,负责给组件对象添加额外的职责。 🥛🍬🍫 (比如:牛奶、糖、巧克力酱)

代码示例:

// 组件接口:咖啡
public interface Coffee {
    String getDescription(); // 获取描述
    double getCost(); // 获取价格
}

// 具体组件:原味咖啡
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "原味咖啡";
    }

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

// 装饰器:调味品
public 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();
    }
}

// 具体装饰器:牛奶
public class Milk extends CoffeeDecorator {
    public Milk(Coffee coffee) {
        super(coffee);
    }

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

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

// 具体装饰器:糖
public class Sugar extends CoffeeDecorator {
    public Sugar(Coffee coffee) {
        super(coffee);
    }

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

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

// 客户端
public class Client {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee(); // 创建原味咖啡
        System.out.println(coffee.getDescription() + ", 价格:" + coffee.getCost());

        coffee = new Milk(coffee); // 加牛奶
        System.out.println(coffee.getDescription() + ", 价格:" + coffee.getCost());

        coffee = new Sugar(coffee); // 加糖
        System.out.println(coffee.getDescription() + ", 价格:" + coffee.getCost());
    }
}

分析:

  • Coffee 是组件接口,定义了咖啡的描述和价格。
  • SimpleCoffee 是具体组件,实现了原味咖啡。
  • CoffeeDecorator 是装饰器,组合了咖啡对象,并实现了咖啡接口。
  • MilkSugar 是具体装饰器,分别给咖啡添加了牛奶和糖。

输出结果:

原味咖啡, 价格:10.0
原味咖啡, 加牛奶, 价格:12.0
原味咖啡, 加牛奶, 加糖, 价格:13.0

四、 装饰器模式的优缺点

优点:

  • 扩展性好 ➕!
  • 灵活性高 🤸!
  • 符合开闭原则 🆕!
  • 避免了继承带来的类爆炸问题 💥!

缺点:

  • 增加了系统的复杂度 😫!
  • 可能会产生很多小对象 👶!
  • 调试困难,特别是当有很多装饰器的时候 🐛!

五、 装饰器模式的应用场景

  • 动态地给对象添加职责: 就像给咖啡加料,或者给汽车加装配件 🚗!
  • 需要灵活地组合不同的功能: 就像给文本编辑器添加不同的功能,比如加粗、斜体、下划线 📝!
  • 避免创建大量的子类: 就像避免为每种加料的咖啡都创建一个新的类 ☕️+🥛, ☕️+🍬, ☕️+🍫!
  • IO流: Java IO流中大量使用了装饰器模式,例如 BufferedInputStreamBufferedOutputStream 都是装饰器,用于提高IO效率。

六、 装饰器模式 vs 代理模式

代理模式请看:(六)趣学设计模式 之 代理模式!

特性装饰器模式代理模式
目的动态地给对象添加额外的职责 ➕控制对对象的访问 👮
关注点扩展功能控制访问
关系装饰器和组件之间是“is-a”关系(接口)代理和真实对象之间是“is-a”关系(接口)
组合装饰器组合的是组件对象,可以多层组合 ☕️+🥛+🍬代理组合的是真实对象,通常只有一层 🧑‍💼+🏠
透明性客户端通常知道它正在使用装饰器 👁️客户端通常不知道它正在使用代理 🙈
例子咖啡加料 ☕️+🥛+🍬房产中介 🧑‍💼+🏠
常见应用IO流,GUI组件远程代理,虚拟代理,保护代理,缓存代理
核心区别扩展对象的功能,不改变原有接口控制对对象的访问,可以改变原有接口的行为

七、 总结

  • 装饰器模式就像给对象穿衣服,让它拥有不同的功能! 🧥
  • 主要包含组件、具体组件、装饰器和具体装饰器四个角色! 🎭
  • 优点是扩展性好、灵活性高、符合开闭原则、避免类爆炸! 👍
  • 缺点是增加复杂度、可能产生很多小对象、调试困难! 👎
  • 适用于需要动态地给对象添加职责,并且需要灵活地组合不同的功能的场景! 🎯

希望这篇文章能让你彻底理解装饰器模式! 💯 祝你学习愉快! 😄
看完请看:(九)趣学设计模式 之 桥接模式!

;