结构型模式——装饰者模式(二)
该项目源码地址:https://github.com/lastwhispers/code/tree/master/java-basic/design-pattern
(设计模式相关代码与笔记)
1. 定义
在不改变原有对象的基础之上,动态地给一个对象添加一些额外的职责。
2. 适用场景
- 扩展一个类的功能或给一个类添加附加职责
- 动态的给一个对象添加功能,这些功能可以再动态的撤销
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
例如:在java的I/O类中有很深刻的体现。
3. 类图与角色
装饰者模式类图- Component(被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。 - ConcreteComponent(具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。 - Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。 - ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
4. 相关设计模式
装饰者模式和代理模式
- 装饰者模式:关注在一个对象上动态添加方法。
- 代理模式:关注在控制对对象的访问,代理模式的代理类可以对客户隐藏一个对象具体的信息。
- 在使用代理模式的时候,常常在一个代理对象中创建对象的实例。而当我们在使用装饰者模式时,通常会把原始对象作为参数,传给装饰者的构造器
装饰者模式和适配器模式
- 装饰者模式和适配器模式都叫做包装模式。在装饰者模式中,装饰者与被装饰者都可以实现相同的接口,或者装饰者是被装饰者的子类。在适配器模式中,适配类与被适配类具有不同的接口,当然也有可能有部分接口是重合的。
5. 模式实例
背景:公司门口有一个小摊卖煎饼的,点了煎饼之后往往还可以在这个基础之上增加一些配料,例如煎蛋,火腿等等,每个配料的价格都不一样,不管你怎么配配料,最终价格是煎饼基础价加上每一种所选配料价格的总和。
5.1 V1版本
(1)相关类
有一个煎饼类:
public class Battercake {
protected String getDesc() {
return "煎饼";
}
protected int cost() {
return 8;
}
}
如果此时需要一个加鸡蛋的煎饼,只需要让煎饼加鸡蛋类继承煎饼类即可。
煎饼加鸡蛋类:
public class BattercakeWithEgg extends Battercake {
@Override
public String getDesc() {
return super.getDesc()+"加一个鸡蛋";
}
@Override
public int cost() {
return super.cost()+1;
}
}
如果此时需要一个加鸡蛋加香肠的煎饼,只需要让煎饼加鸡蛋加香肠类继承煎饼加鸡蛋类即可。
煎饼加鸡蛋加香肠类:
public class BattercakeWithEggSausage extends BattercakeWithEgg{
@Override
public String getDesc() {
return super.getDesc()+"一个香肠";
}
@Override
public int cost() {
return super.cost()+2;
}
}
(2)测试
public class Test {
public static void main(String[]args){
Battercake battercake = new Battercake();
System.out.println(battercake.getDesc()+"销售价格:"+battercake.cost());
BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getDesc()+"销售价格:"+battercakeWithEgg.cost());
BattercakeWithEggSausage battercakeWithEggSausage = new BattercakeWithEggSausage();
System.out.println(battercakeWithEggSausage.getDesc()+"销售价格:"+battercakeWithEggSausage.cost());
}
}
测试结果:
测试结果此时类图,单纯的继承结构:
类图如果我们此时想要一个煎饼加两个鸡蛋,就需要重新继承煎饼类Battercake,生成一个“加两个鸡蛋的煎饼类”。加三个鸡蛋...以此类推,每加一种类型的煎饼就会多生成一个类。下面使用装饰者模式进行改造。
5.2 V2版本
现在,我们用装饰者模式来改写V1版本:
(1)相关类
Component 有一个抽象的煎饼类:
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
ConcreteComponent 煎饼类继承于上面的抽象兼饼类:
public class Battercate extends ABattercake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int cost() {
return 8;
}
}
Decorator 我们让抽象的装饰者类继承抽象的实体:
public class AbstractDecorator extends ABattercake{
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}
@Override
protected String getDesc() {
return aBattercake.getDesc();
}
@Override
protected int cost() {
return aBattercake.cost();
}
}
ConcreteDectrator 煎饼添加鸡蛋的装饰类继承于抽象的装饰类:
public class EggDecorator extends AbstractDecorator{
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost()+1;
}
}
ConcreteDectrator 煎饼添加香肠的装饰类,继承于抽象的装饰类:
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一根香肠";
}
@Override
protected int cost() {
return super.cost()+2;
}
}
(2)测试
public class Test {
public static void main(String[]args){
ABattercake aBattercake;
aBattercake = new Battercate();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc()+"价格为:"+aBattercake.cost());
}
}
测试结果:
测试结果此时不管想加多少个鸡蛋还是香肠,都可以很灵活的添加上。
此时的类图:
V2版本类图6. 优缺点
优点:
- 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
- 符合开闭原则
缺点:
- 会出现更多的代码,更多的类,增加程序复杂性
- 动态装饰时,多层装饰时会使程序更复杂
7. 扩展-JDK1.7以及框架源码中的装饰者模式
7.1 Java I/O
Reader与java.io.BufferedReader
Reader BufferedReaderReader
是被装饰者,BufferedReader
是装饰者
InputStream与FilterInputStream
InputStream FilterInputStreamInputStream
是被装饰者,FilterInputStream
是装饰者
下面是InputStream相关类图的装饰者模式体现。
InputStream类结构7.2 Spring
TransactionAwareCacheDecorator
Cache TransactionAwareCacheDecorator7.3 Spring-session
org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper
7.4 Tomcat
javax.servlet.ServletRequestWrapper
ServletRequestWrapper7.5 Mybatis
org.apache.ibatis.cache.Cache
org.apache.ibatis.cache.Cache