(八)趣学设计模式 之 装饰器模式!
目录
- 一、 啥是装饰器模式?
- 二、 为什么要用装饰器模式?
- 三、 装饰器模式的实现方式
- 四、 装饰器模式的优缺点
- 五、 装饰器模式的应用场景
- 六、 装饰器模式 vs 代理模式
- 七、 总结
🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解适配器模式请看: (七)趣学设计模式 之 适配器模式!
这篇文章带你详细认识一下设计模式中的装饰器模式
一、 啥是装饰器模式?
想象一下,你点了一杯咖啡 ☕️,觉得味道有点单调,想加点料,比如加一份牛奶 🥛,或者加一份糖 🍬,甚至加一份巧克力酱 🍫。 每次加料,咖啡的味道都会变得不一样 😋。
装饰器模式,就是用来动态地给一个对象添加一些额外的职责! 它可以让你在不修改原有对象的基础上,扩展对象的功能 ➕。
简单来说,就是给对象穿上不同的“衣服”,让它拥有不同的功能! 🧥👔
- 你想给一个对象添加一些额外的功能,但是不想修改它的代码: 就像你想给咖啡加料,但是不想修改咖啡的制作方法 ☕️!
- 你想动态地给对象添加功能,而不是静态地继承: 就像你想根据自己的喜好,随时给咖啡加不同的料 🥛🍬🍫!
- 你想避免创建大量的子类: 就像你不想为每种加料的咖啡都创建一个新的类 ☕️+🥛, ☕️+🍬, ☕️+🍫!
二、 为什么要用装饰器模式?
用装饰器模式,好处多多 👍:
- 扩展性好: 可以动态地添加新的装饰器,扩展对象的功能 ➕!
- 灵活性高: 可以灵活地组合不同的装饰器,实现不同的功能组合 🤸!
- 符合开闭原则: 可以在不修改原有代码的情况下,增加新的装饰器,扩展功能 🆕!
- 避免了继承带来的类爆炸问题: 不需要创建大量的子类,减少了类的数量 💥!
三、 装饰器模式的实现方式
装饰器模式主要包含以下几个角色:
- Component(组件): 定义一个对象接口,可以给这些对象动态地添加职责。 ☕️ (比如:咖啡)
- ConcreteComponent(具体组件): 定义一个具体的对象,实现了组件接口。 ☕️ (比如:原味咖啡)
- Decorator(装饰器): 包含一个指向组件对象的引用,并定义一个与组件接口一致的接口。 🧥 (比如:调味品)
- ConcreteDecorator(具体装饰器): 具体的装饰器类,负责给组件对象添加额外的职责。 🥛🍬🍫 (比如:牛奶、糖、巧克力酱)
代码示例:
// 组件接口:咖啡
public interface Coffee {String getDescription(); // 获取描述double getCost(); // 获取价格
}// 具体组件:原味咖啡
public class SimpleCoffee implements Coffee {@Overridepublic String getDescription() {return "原味咖啡";}@Overridepublic double getCost() {return 10.0;}
}// 装饰器:调味品
public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee; // 组合咖啡对象public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}@Overridepublic String getDescription() {return coffee.getDescription();}@Overridepublic double getCost() {return coffee.getCost();}
}// 具体装饰器:牛奶
public class Milk extends CoffeeDecorator {public Milk(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return super.getDescription() + ", 加牛奶";}@Overridepublic double getCost() {return super.getCost() + 2.0;}
}// 具体装饰器:糖
public class Sugar extends CoffeeDecorator {public Sugar(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return super.getDescription() + ", 加糖";}@Overridepublic 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
是装饰器,组合了咖啡对象,并实现了咖啡接口。Milk
和Sugar
是具体装饰器,分别给咖啡添加了牛奶和糖。
输出结果:
原味咖啡, 价格:10.0
原味咖啡, 加牛奶, 价格:12.0
原味咖啡, 加牛奶, 加糖, 价格:13.0
四、 装饰器模式的优缺点
优点:
- 扩展性好 ➕!
- 灵活性高 🤸!
- 符合开闭原则 🆕!
- 避免了继承带来的类爆炸问题 💥!
缺点:
- 增加了系统的复杂度 😫!
- 可能会产生很多小对象 👶!
- 调试困难,特别是当有很多装饰器的时候 🐛!
五、 装饰器模式的应用场景
- 动态地给对象添加职责: 就像给咖啡加料,或者给汽车加装配件 🚗!
- 需要灵活地组合不同的功能: 就像给文本编辑器添加不同的功能,比如加粗、斜体、下划线 📝!
- 避免创建大量的子类: 就像避免为每种加料的咖啡都创建一个新的类 ☕️+🥛, ☕️+🍬, ☕️+🍫!
- IO流: Java IO流中大量使用了装饰器模式,例如
BufferedInputStream
和BufferedOutputStream
都是装饰器,用于提高IO效率。
六、 装饰器模式 vs 代理模式
代理模式请看:(六)趣学设计模式 之 代理模式!
特性 | 装饰器模式 | 代理模式 |
---|---|---|
目的 | 动态地给对象添加额外的职责 ➕ | 控制对对象的访问 👮 |
关注点 | 扩展功能 | 控制访问 |
关系 | 装饰器和组件之间是“is-a”关系(接口) | 代理和真实对象之间是“is-a”关系(接口) |
组合 | 装饰器组合的是组件对象,可以多层组合 ☕️+🥛+🍬 | 代理组合的是真实对象,通常只有一层 🧑💼+🏠 |
透明性 | 客户端通常知道它正在使用装饰器 👁️ | 客户端通常不知道它正在使用代理 🙈 |
例子 | 咖啡加料 ☕️+🥛+🍬 | 房产中介 🧑💼+🏠 |
常见应用 | IO流,GUI组件 | 远程代理,虚拟代理,保护代理,缓存代理 |
核心区别 | 扩展对象的功能,不改变原有接口 | 控制对对象的访问,可以改变原有接口的行为 |
七、 总结
- 装饰器模式就像给对象穿衣服,让它拥有不同的功能! 🧥
- 主要包含组件、具体组件、装饰器和具体装饰器四个角色! 🎭
- 优点是扩展性好、灵活性高、符合开闭原则、避免类爆炸! 👍
- 缺点是增加复杂度、可能产生很多小对象、调试困难! 👎
- 适用于需要动态地给对象添加职责,并且需要灵活地组合不同的功能的场景! 🎯
希望这篇文章能让你彻底理解装饰器模式! 💯 祝你学习愉快! 😄
看完请看:(九)趣学设计模式 之 桥接模式!