装饰器模式(Decorator Pattern)
装饰器模式也叫做包装模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供比继承更有弹性的替代方案(扩展原有对象的功能),强调一点是基于已有对象的功能增强;装饰器模式属于结构性模式;
装饰器类图
Component 抽象构件
定义一个抽象接口以规范准备接受附加责任的对象
1 2 3
| public abstract class Component { public abstract void operation(); }
|
ConcreteComponent 具体的构件
实现抽象构件,通过装饰角色为其添加一些职责
1 2 3 4 5 6
| public class ConcreteComponent extends Component{ @Override public void operation() { System.err.println("Do some biz event!"); } }
|
Decorator 抽象装饰角色
继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public abstract class Decorator extends Component{
protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public void operation() { component.operation(); } }
|
ConcreteDecoratorA
实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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 测试类
1 2 3 4 5 6 7
| public class Client { public static void main(String[] args) { Component component = new ConcreteComponent(); Decorator decoratorA = new ConcreteDecoratorA(component); decoratorA.operation(); } }
|
举个例子
买煎饼的时候,我们可以直接买一个煎饼,但有的时候觉得味道单一或者吃不饱,我们可以加一些东西,比如烤肠,鸡蛋,鸡排等等;
先看看非装饰器模式的写法
BatterCake
就一个普通的煎饼类;
1 2 3 4 5 6
| public class BatterCake { protected String getMsg(){return "煎饼";} public int getPrice(){ return 10; } }
|
BatterCakeWithEgg
添加一个鸡蛋的煎饼;
1 2 3 4 5 6 7 8 9
| public class BatterCakeWithEgg extends BatterCake{ protected String getMsg(){ return super.getMsg() + " 加一个鸡蛋"; }
public int getPrice(){ return super.getPrice() + 1; } }
|
BatterCakeWithEggAndSauage
加一个鸡蛋 再加一跟烤肠的煎饼;
1 2 3 4 5 6 7 8 9
| public class BatterCakeWithEggAndSauage extends BatterCakeWithEgg{ protected String getMsg(){ return super.getMsg() + " 加一个烤肠"; }
public int getPrice(){ return super.getPrice() + 2; } }
|
Client 客户
1 2 3 4 5 6 7 8 9 10 11 12
| 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 抽象组件
1 2 3 4
| public abstract class BatterCake { protected abstract String getMsg(); protected abstract int getPrice(); }
|
基础类 实现抽象接口
1 2 3 4 5 6 7 8 9 10 11
| public class BaseBatterCake extends BatterCake{ @Override protected String getMsg() { return "煎饼"; }
@Override protected int getPrice() { return 5; } }
|
BatterCakeDecorator 装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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 具体的装饰器对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 具体的装饰对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; } }
|
客户 测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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方法的基础上添加自己的逻辑,同时具体的装饰对象返回父类类型的对象;
1 2 3 4
| @Override protected int getPrice() { return super.getPrice() + 2; }
|
这里进行一下总结:
1、具体装饰对象(EggDecorator)一定是继承自装饰组件(BatterCakeDecorator)
2、为了实现对象增强,子类中的方法一定是基于super方法的基础上,添加自己的逻辑的
使用场景
- 用于扩展一个类的功能或给一个类添加附加职责
- 动态地给一个对象添加功能,这些功能可以在动态的撤销
实际应用
Spring TransactionAwareCacheManager
JDK FileInputStream
装饰器模式与代理模式
- 装饰器模式是一种特殊的代理模式
- 装饰器模式强调自身的功能扩展,透明的,动态的扩展与增强
透明指的是功能的扩展由客户端控制
- 代理模式强调代理过程的控制
装饰器模式的优点
1、装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
2、通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
3、装饰器模式完全遵守开闭原则
装饰器模式的缺点
1、增加了一些子类,系统代码会显得臃肿。
2、组合方式容易出错,代码可读性比较差。
参考文档
1、包装模式就是这么简单啦
2、装饰器模式(装饰设计模式)详解
3、java中的装饰设计模式,浅谈与继承之间的区别
4、JDK IO中的适配器模式和装饰者模式