一、定义与类型:
》定义:在不改变原有对象的基础之上,将功能附加到已有对象上
》提供了比继承更有弹性的替代方案(扩展原有对象功能)
》类型:结构型
二、装饰者——使用场景
生活场景,比如买礼品时,需要礼品和层层包装,包装可以自行选取后的总花销;买早饭,是肉夹馍还是一个肉夹馍再额外加蛋还是火腿时的售价计算;买蛋糕时,不同层次选择的配料不同,如何计算其价格等
程序场景:扩展一个类的功能或给一个类添加附加职责;
动态的给一个对象添加功能,这些功能可以再动态的撤销。
三、优点:
》继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能;
——注意:继承属于扩展方式之一,但不一定是弹性设计的最佳实现;并且,继承实现扩展功能,需要提前考虑到所有功能,将可能性通过代码对应出来,也就是说在编译时就已经知道可能性。而装饰者模式在编译时并不能确定用户的操作,以及可能性,属于运行期动态添加改变的功能。
》通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果;
》符合开闭原则。使用装饰者模式,可以扩展对象的行为,装饰者和被装饰者可以独立地变化,原有代码不用改变。程序来说,装饰把类中的者模式就是装饰功能移到对象之外,
四、缺点:
》会出现更多的代码,更多的类,增加程序复杂性;
》动态装饰时,多层装饰时会更复杂;装饰者模式会比继承使用的类少,但是使用到的对象会比继承要多。排查问题时会比较困难,复杂性强。
五、装饰者——相关设计模式
》装饰者模式和代理模式:装饰者模式关注于对象动态地添加功能,而代理模式关注于控制对对象的访问,代理模式中的代理类可以对客户隐藏对象的信息,代理模式中通常是在代理类中创建一个对象的实例,而装饰者模式通常是把被装饰对象(也叫做原始对象)作为一个参数传递给装饰类的构造器。
》装饰者模式和适配器模式:都可以叫做包装模式wrapper,而装饰者模式中的包装者和被包装者可以实现相同的接口,或者装饰者是被装饰者的子类,而适配器模式中适配器和被适配者一般不是同一个接口。扩展说,装饰者模式还可以推广为半装饰模式,比如装饰者除了实现和被装饰者同一个接口外,还可以实现其他接口(或者装饰者和被装饰者继承同一个类,但装饰者继承了实现了其他接口),不同的装饰者实现了其他不同的接口,这个可以被称为半装饰模式。
六、代码实践:
示例一:初级版本:
场景:商家卖煎饼,可以搭配不同的辅料,鸡蛋或者煎饼,计算价格,如下:
package com.zxl.design.pattern.structural.decorator.v1;
/**
* Created by Administrator on 2019/7/20.
*煎饼类
*/
public class Battercake {
public String getDesc(){
return "煎饼";
}
public int cost(){
return 8;
}
}
package com.zxl.design.pattern.structural.decorator.v1;
/**
* Created by Administrator on 2019/7/20.
* 装饰者:鸡蛋
*/
public class BattercakewithEgg extends Battercake{
public String getDesc(){
return super.getDesc()+"加一个鸡蛋";
}
public int cost(){
return super.cost()+1;
}
}
package com.zxl.design.pattern.structural.decorator.v1;
/**
* Created by Administrator on 2019/7/20.
* 装饰者:鸡蛋和香肠
*/
public class BattercakewithEggSausage extends BattercakewithEgg{
public String getDesc(){
return super.getDesc()+"加一个香肠";
}
public int cost(){
return super.cost()+2;
}
}
package com.zxl.design.pattern.structural.decorator.v1;
/**
* Created by Administrator on 2019/7/20.
*/
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());
}
}
"D:\Program Files\Java\jdk1.8.0_102\bin\java" "-javaagent:D:\InteliijIDea\IntelliJ IDEA 2017.1.4\lib\idea_rt.jar=51497:D:\InteliijIDea\IntelliJ IDEA 2017.1.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_102\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\rt.jar;C:\Users\Administrator\Desktop\gson-master\gson-master\DesignMode\target\classes" com.zxl.design.pattern.structural.decorator.v1.Test
煎饼销售价格:8
煎饼加一个鸡蛋销售价格:9
煎饼加一个鸡蛋加一个香肠销售价格:11
运行结果如上,满足基本要求,但是可以看到,如果辅料不一样,用户可能还需要2个鸡蛋加一个煎饼,那如上就无法实现,怎么办呢?
看下面的优化升级,v2版本:
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public abstract class ABattercake {
public abstract String getDesc();
public abstract int cost();
}
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public class Battercake extends ABattercake{
public String getDesc() {
return "煎饼";
}
public int cost() {
return 8;
}
}
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public class AbstractDecorator extends ABattercake{
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake){
this.aBattercake = aBattercake;
}
public String getDesc() {
return this.aBattercake.getDesc();
}
public int cost() {
return this.aBattercake.cost();
}
}
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
public String getDesc() {
return super.getDesc()+"加一个鸡蛋";
}
@Override
public int cost() {
return super.cost()+1;
}
}
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
public String getDesc() {
return super.getDesc()+"加一个香肠";
}
@Override
public int cost() {
return super.cost()+2;
}
}
package com.zxl.design.pattern.structural.decorator.decorator.v2;
/**
* Created by Administrator on 2019/7/20.
*/
public class Test {
public static void main(String[] args) {
ABattercake aBattercake;
aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc()+"销售价格:"+aBattercake.cost());
}
}
煎饼加一个鸡蛋加一个鸡蛋加一个香肠销售价格:12
Process finished with exit code 0
运行结果和UML图如上
如上v2版本,程序更加具备可扩展性,可以根据用户的需求轻松调整。
注:如上UML图中11 表示一个抽象类中有一个抽象的煎饼对象。
扩展:
如上,抽象的装饰类AbstractDecrator类中如果定义为抽象类,其中定义一个抽象的doSomething()方法,
则装饰者的具体实现类,可以根据自己的需求进行适当地扩充功能。如上即为半装饰模式。
七、装饰者模式源码解析:
1、jdk中,最明显的是其中的 IO流方面的内容,比如reader类,扩展有bufferedReader。
还有inputStream 相关的,对应的有输出bufferdWritter outputstream等等。
2、在Spring框架中,有个处理缓存和同步事务相关的类TransactionAwareCacheDecorator。该类主要是出来spring中缓存和同步处理级别相关的问题
3、在spring框架中SessionRepositoryRequestWrapper(HttpServelet httpServelet)方法
4、mybatis中装饰者模式的使用 ,比如Cache 相关的一些类。