设计模式系列之二:装饰者模式(Decorator Pattern)

这一阵还是比较忙,说好的两天一更都达不到,看来自己还是得努力。

装饰者模式其实普遍存在于我们的生活抽象中。现存的一些资料是以一个星巴克咖啡店来举例说明的,这里我们就用煎饼果子来举例吧,生动形象。

一个煎饼果子(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体系的输入输出流,同样也是这样的设计模式,在此不作赘述了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值