目录
1、场景直入
有一个煎饼果子类,
动态需求有加一个蛋、加两个蛋、加辣椒、加葱、加香菜、还有加香肠火腿等等。不同的需求对应了不同的价格。
那我们如果针对每一种需求,都声明一个煎饼果子的子类,那需要多少类?
煎饼加一个蛋类、煎饼加一个香肠类、煎饼加两个蛋类、煎饼加两个蛋两个香肠类。。。学过排列组合的老师出来一下!
可以看到,单纯使用继承,在类似场景中会导致类的数目爆炸式增长。
2、解决方案
核心是把蛋、香肠、辣椒等这些需求都单独建立类,然后再和煎饼果子基础类产生组合,组合后保证产生的类还得是煎饼果子类。这就像一个人穿的衣服,长裙、短裙、裤子衬衫就像人的装饰,人始终是主体,一个人穿上漂亮的短裙还是人吗?当然是。穿上超短裤还是人吗?当然是。不穿呢?。。。
首先,有一个煎饼果子抽象类AbstractBatterCake
然后,再来一个装饰抽象类AbstractDecorator,该类继承自煎饼果子抽象类AbstractBatterCake,保证任何的装饰之后,得到的仍然是煎饼果子类。另外,构造方法的参数也要是煎饼果子抽象类,即从哪个类的基础上进行装饰,这样保证了装饰类的连续可动态叠加。
其次,依次具体化实现装饰抽象类AbstractDecorator。
3、具体代码
一定要先看关键的测试类:
package structural.decorator.v2;
public class Test {
public static void main(String[] args) {
AbstractBatterCake batterCake;
batterCake=new BatterCake();
batterCake=new EggDecorator(batterCake);
batterCake=new EggDecorator(batterCake);
batterCake=new SausageDecorator(batterCake);
System.out.println(batterCake.getDesc()+" 销售价格:"+batterCake.price());
}
}
然后是抽象煎饼类:
package structural.decorator.v2;
//抽象煎饼类
public abstract class AbstractBatterCake {
protected abstract String getDesc();
protected abstract int price();
}
基本煎饼子类:
package structural.decorator.v2;
//煎饼
public class BatterCake extends AbstractBatterCake{
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int price() {
return 8;
}
}
装饰抽象类:
package structural.decorator.v2;
public abstract class AbstractDecorator extends AbstractBatterCake{
private AbstractBatterCake battercake;
public AbstractDecorator(AbstractBatterCake batterCake) {
this.battercake=batterCake;
}
//public abstract void dosomthing();
@Override
protected String getDesc() {
return this.battercake.getDesc();
}
@Override
protected int price() {
return this.battercake.price();
}
}
加蛋装饰类:
package structural.decorator.v2;
//加蛋 装饰
public class EggDecorator extends AbstractDecorator{
public EggDecorator(AbstractBatterCake batterCake) {
super(batterCake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一个鸡蛋";
}
@Override
protected int price() {
return super.price()+1;
}
}
香肠装饰类:
package structural.decorator.v2;
//加香肠 装饰
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(AbstractBatterCake batterCake) {
super(batterCake);
}
@Override
protected String getDesc() {
return super.getDesc()+"加一根香肠";
}
@Override
protected int price() {
// TODO Auto-generated method stub
return super.price()+2;
}
}
4、升华总结
我们从代码中看到,在基础的煎饼类上我们可以不断的叠加装饰,使之具有更加丰富的料。但是,当前的装饰类只是进行了对煎饼基本方法的重写。当然,我们可以在装饰类中新增新的方法,使得煎饼拥有更加丰富的特性。
(1)装饰者模式定义:
在不改变原有对象的基础之上,将功能附加到对象上。
提供了比继承更有弹性的替代方案(扩展原有对象功能)。
(2)使用场景:
扩展一个类的功能或给一个类添加附加职责
动态的给一个类添加功能,这些功能可以再动态的撤销。
(3)优点:
继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。
可以将多个类中重复的功能代码单独拿出来,实现复用,降低基础类的复杂性。
通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同的效果。
符合开闭原则。
(4)缺点:
会出现更多的代码,更多的类,增加程序复杂性。
动态装饰时、多层装饰时会更复杂。比如在煎饼果子的基础上,现在又有了包子、面条等食物,这样就会更加复杂,不过这不是设计模式本身带来的复杂性,是业务逻辑复杂后引起的代码复杂性。
另外,你也可以参考 装饰者模式在坦克大战代码中的应用 ,文章中对装饰者模式进行了总结,并且对实际应用中遇到的一些问题和注意事项作了介绍。
5、源码解析
比如我们看Java IO中的BufferedReader,该类继承自Reader抽象类,同时在该类中使用带参构造参数Reader,传入修饰前的类。
同样,我们也可以看到BufferedInputStream与InputStream之间的关系。