装饰器模式

# 装饰器模式(Decorator Pattern) 装饰器模式也叫做包装模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供比继承更有弹性的替代方案(扩展原有对象的功能), 强调一点是基于已有对象的功能增强; 装饰器模式属于结构性模式; ## 装饰器类图 ![](https://oscimg.oschina.net/oscnet/up-b2f04bf843bb9c3e787016e216e5fbd91cf.png) ### Component 抽象构件 > 定义一个抽象接口以规范准备接受附加责任的对象 ```text public abstract class Component { public abstract void operation(); } ``` ### ConcreteComponent 具体的构件 > 实现抽象构件,通过装饰角色为其添加一些职责 ```text public class ConcreteComponent extends Component{ @Override public void operation() { System.err.println("Do some biz event!"); } } ``` ### Decorator 抽象装饰角色 > 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能 ```text public abstract class Decorator extends Component{ //持有组件对象 protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { //转发请求给组件对象,可以在转发前后执行一些附加动作 component.operation(); } } ``` ### ConcreteDecoratorA > 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任 ```text public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } public void operationFirst() { System.err.println("ConcreteDecoratorA first!"); } public void operationLast() { System.err.println("ConcreteDecoratorA last!"); } @Override public void operation() { operationFirst(); super.operation(); operationLast(); } } ``` ### Client 测试类 ```text public class Client { public static void main(String[] args) { Component component = new ConcreteComponent(); Decorator decoratorA = new ConcreteDecoratorA(component); decoratorA.operation(); } } ``` ## 举个例子 买煎饼的时候,我们可以直接买一个煎饼,但有的时候觉得味道单一或者吃不饱,我们可以加一些东西,比如烤肠,鸡蛋,鸡排等等; **先看看非装饰器模式的写法** ### BatterCake > 就一个普通的煎饼类; ```text public class BatterCake { protected String getMsg(){return "煎饼";} public int getPrice(){ return 10; } } ``` ### BatterCakeWithEgg > 添加一个鸡蛋的煎饼; ```text public class BatterCakeWithEgg extends BatterCake{ protected String getMsg(){ return super.getMsg() + " 加一个鸡蛋"; } public int getPrice(){ return super.getPrice() + 1; } } ``` ### BatterCakeWithEggAndSauage > 加一个鸡蛋 再加一跟烤肠的煎饼; ```text public class BatterCakeWithEggAndSauage extends BatterCakeWithEgg{ protected String getMsg(){ return super.getMsg() + " 加一个烤肠"; } public int getPrice(){ return super.getPrice() + 2; } } ``` ### Client 客户 ```text public class Client { public static void main(String[] args) { BatterCake batterCake = new BatterCake(); System.err.println(batterCake.getMsg() + "价格 " + batterCake.getPrice()); BatterCakeWithEgg batterCakeWithEgg = new BatterCakeWithEgg(); System.err.println(batterCakeWithEgg.getMsg() + "价格 " + batterCakeWithEgg.getPrice()); BatterCakeWithEggAndSauage batterCakeWithEggAndSauage = new BatterCakeWithEggAndSauage(); System.err.println(batterCakeWithEggAndSauage.getMsg() + "价格 " + batterCakeWithEggAndSauage.getPrice()); } } ``` 看到这种写法,应该能看出它的弊端了,就是实现很简单,适合于需求固定的业务和多样性比较简单的业务; 一旦客户需要一个鸡蛋,两根烤肠,那是不是还要在BatterCakeWithEggAndSauage类上继续扩展呢,这其实是很low的设计; 而且非常不灵活,比如客户只需要一跟烤肠的煎饼。这个时候怎么解决呢; **使用装饰器模式优化** ### BatterCake 抽象组件 ```text public abstract class BatterCake { protected abstract String getMsg(); protected abstract int getPrice(); } ``` ### 基础类 实现抽象接口 ```text public class BaseBatterCake extends BatterCake{ @Override protected String getMsg() { return "煎饼"; } @Override protected int getPrice() { return 5; } } ``` ### BatterCakeDecorator 装饰器 ```text public class BatterCakeDecorator extends BatterCake{ private BatterCake batterCake; public BatterCakeDecorator(BatterCake batterCake) { this.batterCake = batterCake; } @Override protected String getMsg() { return this.batterCake.getMsg(); } @Override protected int getPrice() { return this.batterCake.getPrice(); } } ``` ### EggDecorator 具体的装饰器对象 ```text public class EggDecorator extends BatterCakeDecorator{ public EggDecorator(BatterCake batterCake) { super(batterCake); } @Override protected String getMsg() { return super.getMsg() + " 加一个鸡蛋"; } @Override protected int getPrice() { return super.getPrice() + 1; } } ``` ### SauageDecorator 具体的装饰对象 ```text public class SauageDecorator extends BatterCakeDecorator{ public SauageDecorator(BatterCake batterCake) { super(batterCake); } @Override protected String getMsg() { return super.getMsg() + " 加一个烤肠"; } @Override protected int getPrice() { return super.getPrice() + 2; } } ``` ### 客户 测试类 ```text public class Client { public static void main(String[] args) { BatterCake batterCake = new BaseBatterCake(); System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice()); batterCake = new EggDecorator(batterCake); System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice()); batterCake = new SauageDecorator(batterCake); System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice()); batterCake = new EggDecorator(batterCake); System.err.println(batterCake.getMsg() + "总计 : " + batterCake.getPrice()); } } ``` 上面这种写法是运用了装饰器模式的写法,这样会增加程序的灵活性,EggDecorator返回一个BatterCake,在EggDecorator 中的getMsg方法和getPrice方法中添加关于Egg的逻辑来实现对BatterCake的增强,同理SauageDecorator也是,在它自己的getMsg方法和 getPrice方法中添加自己的逻辑,当然,都是基于调用super方法的基础上添加自己的逻辑,同时具体的装饰对象返回父类类型的对象; ```text @Override protected int getPrice() { return super.getPrice() + 2; } ``` **这里进行一下总结:** 1、具体装饰对象(EggDecorator)一定是继承自装饰组件(BatterCakeDecorator) 2、为了实现对象增强,子类中的方法一定是基于super方法的基础上,添加自己的逻辑的 ## 使用场景 - 用于扩展一个类的功能或给一个类添加附加职责 - 动态地给一个对象添加功能,这些功能可以在动态的撤销 ## 实际应用 Spring TransactionAwareCacheManager JDK FileInputStream ## 装饰器模式与代理模式 - 装饰器模式是一种特殊的代理模式 - 装饰器模式强调自身的功能扩展,透明的,动态的扩展与增强 > 透明指的是功能的扩展由客户端控制 - 代理模式强调代理过程的控制 ## 装饰器模式的优点 1、装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用 2、通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果 3、装饰器模式完全遵守开闭原则 ## 装饰器模式的缺点 1、增加了一些子类,系统代码会显得臃肿。 2、组合方式容易出错,代码可读性比较差。 ## 参考文档 1、[包装模式就是这么简单啦](https://juejin.cn/post/6844903603178569741) 2、[装饰器模式(装饰设计模式)详解](http://c.biancheng.net/view/1366.html) 3、[java中的装饰设计模式,浅谈与继承之间的区别](https://www.cnblogs.com/losedMemory/p/6246029.html) 4、[JDK IO中的适配器模式和装饰者模式](https://blog.csdn.net/lsgqjh/article/details/63254876)