这一阵还是比较忙,说好的两天一更都达不到,看来自己还是得努力。
装饰者模式其实普遍存在于我们的生活抽象中。现存的一些资料是以一个星巴克咖啡店来举例说明的,这里我们就用煎饼果子来举例吧,生动形象。
一个煎饼果子(Pancake rolled with crisp fritter 百度翻译。。不妨称之为Pancake)值5元,那么此时我加一个蛋(1元吧,虽然不切实际),总价为6元。
我加一个肠(姑且2元)呢?那就是5+2=7元
一个肠和一个蛋呢?5+1+2=8元
两个肠和一个蛋呢?5+4+1=10元
土豪级套餐n个肠n个蛋呢。。
看完这个,相信你大概马上想到了如下结构:
/**
* Created by youweixi on 15/11/22.
* 煎饼果子
*/
public class Pancake {
/**
* 煎饼价格
* */
private static int price = 5;
/**
* 加蛋数量
* */
private int eggNum = 0;
/**
* 加肠数量
* */
private int hotdogNum = 0;
/**
* 价格
* */
private int getCost(){
return price + eggNum * 1 + hotdogNum * 2;
}
}
这样写很完美,对于这个问题这个类很适合。
现在煎饼果子要拓展业务,要做出如下价格改变。
1. 新增可以加培根
2. 蛋的价格调成3元一个
3. 一种新的煎饼不能加蛋和培根
代码随需求而变。
1. 为了加上培根,我们不得不在主类上面添加Bacon的成员和修改方法,还要修改getCost方法。
2. 为了更改蛋的价格需要更改getCost方法。
3. 第三个问题虽然只是一个约束,但是此时eggNum和baconNum在这个类中就不会用到。
再比如说电商搞活动,打折券有不同的种类,可以叠加用,有的打折券是针对某一类商品,有些打折券是针对所有商品,在这个问题中,恐怕就不能为每一个商品都加上是否应用了打折券的成员变量(也许某一个打折券只有一天有效呢,我们却为他修改了所有的商品)。
基于这种问题,我们逐渐总结出了装饰者模式
其实这个模式相对固定,即对一个类可以重复的用不同的装饰类来装饰,最终通过函数嵌套回调来实现逐层解包的效果。
记住三点即可:
1. 基类和装饰类的基类是相同的->所以可以多次重复打包,装饰类中有超类成员变量
2. 虚方法在基类中已经定义但未实现->所以可以函数回源逐层解包
3. 装饰类和基类的继承类分开但同源->进行扩展时源代码不变,修改时对症下药
有了这个模式,我们就可以重新设计我们的代码来实现我们的煎饼果子铺:
/**
* Created by youweixi on 15/11/22.
* 煎饼果子铺的超类
*/
public abstract class Component {
protected String description = "Do You Know Who I Am ?";
protected abstract int getCost();
protected abstract String getDescription();
}
/**
* Created by youweixi on 15/11/22.
* 煎饼果子类(继承了Component超类,由于我们之后有具体实现类,所以这里定义为抽象类)
*/
public abstract class Pancake extends Component{
/**
* 描述
* */
private String description = "煎饼果子";
/**
* 价格
* */
@Override
public int getCost(){
return 5;
}
@Override
protected String getDescription() {
return this.description;
}
}
/**
* Created by youweixi on 15/11/22.
* 高级版
*/
public class PancakeAverage extends Pancake {
/**
* 价格,高级版要加2元
* */
@Override
public int getCost(){
return super.getCost() + 2;
}
@Override
protected String getDescription() {
return super.getDescription() + ":高级版";
}
}
/**
* Created by youweixi on 15/11/22.
* 普通版
*/
public class PancakeNormal extends Pancake {
@Override
protected String getDescription() {
return super.getDescription() + ":普通版";
}
}
装饰类中间层
/**
* Created by youweixi on 15/11/22.
* 装饰者中间层
*/
public abstract class Decorator extends Component {
}
/**
* Created by youweixi on 15/11/22.
* 培根装饰类
*/
public class DecoratorBacon extends Decorator{
/**
* 超类成员
* */
protected Component component;
/**
* 构造方法
* */
public DecoratorBacon (Component component){
this.component = component;
}
/**
* 培根3元一份
* */
@Override
protected int getCost() {
return component.getCost() + 3;
}
@Override
protected String getDescription() {
return component.getDescription() + " 培根*1";
}
}
/**
* Created by youweixi on 15/11/22.
* 加蛋装饰类
*/
public class DecoratorEgg extends Decorator{
/**
* 超类成员
* */
protected Component component;
/**
* 构造方法
* */
public DecoratorEgg (Component component){
this.component = component;
}
@Override
protected int getCost() {
return component.getCost() + 1;
}
@Override
protected String getDescription() {
return component.getDescription() + " 蛋*1";
}
}
/**
* Created by youweixi on 15/11/22.
* 加肠装饰类
*/
public class DecoratorHotdog extends Decorator{
/**
* 超类成员
* */
protected Component component;
/**
* 构造方法
* */
public DecoratorHotdog (Component component){
this.component = component;
}
@Override
protected int getCost() {
return 0;
}
@Override
protected String getDescription() {
return null;
}
}
至此,整个程序体系就写好了。
调用看一下效果:
public static void main(String[] args){
//购买一个普通版
Component p1 = new PancakeNormal();
//加一个培根
p1 = new DecoratorBacon(p1);
//加一个蛋
p1 = new DecoratorEgg(p1);
System.out.println("Description:" + p1.getDescription() + " Price:" + p1.getCost() + "\n\n");
//购买一个高级版
Component p2 = new PancakeAverage();
//加一个培根
p2 = new DecoratorBacon(p2);
//再加一个培根
p2 = new DecoratorBacon(p2);
//加一个蛋
p2 = new DecoratorEgg(p2);
//加一个肠
p2 = new DecoratorHotdog(p2);
System.out.println("Description:" + p2.getDescription() + " Price:" + p2.getCost());
}
结果正确
Description:煎饼果子:普通版 培根*1 蛋*1 Price:9
Description:煎饼果子:高级版 培根*1 培根*1 蛋*1 热狗*1 Price:16
可以看出这种模式适合抽象为包装类应用,在应用这种设计模式的时候务必理清楚业务逻辑,分清装饰类和主类,具体层次就会清晰很多了。
有兴趣的同学可以具体查一下JAVA IO体系的输入输出流,同样也是这样的设计模式,在此不作赘述了。