装饰者模式
定义及特点
装饰者模式 是不改变原类文件和使用继承的情况下,动态的扩展对象的一个功能,通过创建一个包装对象,也就是装饰来包裹真实的对象。它遵循了开闭原则,对扩展开放,对修改关闭。有以下几个特点:
- 装饰对象和真实对象有相同的接口,这样就方便了装饰对象和真实对象之间的交互
- 装饰对象包含了一个真实对象的引用
- 装饰对象接受了来自所有客户端的请求,并转发给真实对象
- 装饰对象可在转发这些请求之前或之后添加一些附件功能
使用场景
- 扩展一个类的功能
- 动态给一个对象添加功能,同时也可动态撤销
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实
优缺点
优点:
- 装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者可以提供比继承更多的灵活性
- 装饰者模式可使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合
缺点:
- 灵活使用的同时,增加了类的复杂性
- 类的数量繁多,过度使用会使程序变得复杂
- 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
角色组成
- 抽象构件(Component)角色: 给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色: 定义一个将要接收附加责任的类
- 装饰(Decorator)角色: 持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
- 具体装饰(Concrete Decorator)角色: 负责给构件对象添加上附加的责任。
代码示例
星巴克订单问题:
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
- 要求具有良好的扩展性,改动方便、维护方便
- 计算不同种类的费用:客户可单点咖啡,要也可以点单品咖啡+调料组合
具体代码实现: https://gitee.com/ldj123/design-patterns.git
IO流,装饰者模式的实现
DataInputStream dis = new DataInputStream(new FileInputStream("d://abc.txt"));
System.out.println(dis.read());
dis.close();
说明:
- InputStream是抽象类 ,类似代码实现中的Drink
- FileInputStream 是InputStream的子类 类似代码实现中的DeCaf,LongBlack
- FilterInputStream 是InputStream的子类 类似代码实现中的Decorator 修饰者
- DataInputStream 是FilterInputStream 的子类 具体的修饰者 类似代码实现中Soy
- FilterInputStream 类有protected volatile InputStream in 即被修饰者