设计模式之装饰者模式

装饰者概述

动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

问题的抛出

开始的类设计如下:

这里写图片描述
但是有的顾客需要添加调料,比如说豆浆,摩卡等等。还需要根据这些调料收取不同的费用,所以订单系统开始尝试变化,第一个尝试如下:
这里写图片描述
看看这个设计有多么的糟糕,没中不同的饮料加调料的组合都需要生成一个新类,不敢想象会有多少个类存在,况且一旦某个调料价格变动,要改多少的类,维护很困难。
那么再来看看第二种尝试:是利用实例变量和继承,把这些调料全部放在超类中。
这里写图片描述
我们来仔细思考下这种设计的缺陷:
1)调料价格的改变会使我们更改现有的代码
2)一旦出现新的调料,就需要加上新的方法,并改变超类中的cost方法
3)比如有个新的饮料茶,某些调料并不合适(奶泡),但是子类都必须继承haswhip方法。这是很糟糕的事,就像不会叫的鸭子还必须有会飞的方法!
4)利用继承设计子类的行为,是在编译时候就静态决定了,而且所有的子类都会继承相同行为。
我们所需要的系统是能够巧妙的将多个新行为,或者设计超类的时候还没想到的职责加在对象上,而且不用修改代码,为此而努力
由这个想法我们提出新的设计原则:类应该对扩展开放,对修改关闭。这样的设计具有弹性可以应对改变,可以接受新的行为来应对改变的需求。但是我们需要注意在选择需要被扩展的代码部分要十分小心。每个地方都使用开放-关闭原则是一种浪费,也没必要,还会导致代码变得更加复杂且难以理解。

认识装饰者模式

比方说顾客想要摩卡咖啡。
1)先拿一个深焙咖啡对象
2)以摩卡对象装饰它
3)调用cost方法,依赖委托将调料的价格加上去
这里写图片描述
从上图可以看出:
1)装饰者和被装饰者具有相同的超类型
2)可以用一个或者多个装饰者去包装一个对象
3)既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它
4)装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
5)对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
现在我们可以来看看代码了:

package designPattern;

public abstract class Beverage {
    String description ="Unkonwn Beverage";

    public String getDescription(){
        return description;
    }
    //cost()必须在子类中实现
    public  abstract double cost();
}

//必须让Condiment Decorator 能够取代Beverage 所以扩展自Beverage类
abstract class  CondimentDecorator extends  Beverage{
    //所有调料装饰者必须重新实现getDescription方法
    public abstract  String getDescription();
}
//饮料  浓缩咖啡 需要为具体饮料设置描述以及实现cost方法
class Espresso extends Beverage{

        public Espresso(){
            description="Espresso";
        }
    @Override
    public double cost() {
        return 1.99;
    }
}
class HouseBlend extends Beverage{

    public HouseBlend(){
        description="HouseBlend";
    }
    @Override
    public double cost() {
        return 0.99;
    }
}

//装饰者摩卡
class  Mocha extends CondimentDecorator{
    /*思路:
    1)用一个实例变量记录饮料,也就是被装饰者
    2)想办法让被装饰者被记录到实例变量中,用构造函数把饮料当做
    构造函数参数传入到实例变量中
    * */
    Beverage beverage;
    public  Mocha(Beverage beverage){
        this.beverage=beverage;
    }

    //为了完整的描述饮料(包括使用的调料)
    @Override
    public double cost() {
        return 0.2+beverage.cost();
    }

    @Override
    public String getDescription() {
        return beverage.getDescription()+",Mocha";
    }
}

//装饰者 Soy
class  Soy extends CondimentDecorator {
    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public double cost() {
        return 0.6 + beverage.cost();
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Soy";
    }
}
//装饰者 Whip
class  Whip extends CondimentDecorator {
    Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public double cost() {
        return 0.7 + beverage.cost();
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Whip";
    }
}

//下订单了
class StarbuzzCoffee{
    public static void main(String[] args) {
        //制造一杯浓缩咖啡 用Mocha,奶泡,豆浆装饰的
        Beverage beverage=new Espresso();
        beverage=new Soy(beverage);
        beverage=new Mocha(beverage);
        beverage=new Whip(beverage);
        System.out.println(beverage.getDescription()+" $"+beverage.cost());

    }
}

要点

�继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
� 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
� 组合和委托可用于在运行时动态地加上新的行为。
� 除了继承,装饰者模式也可以让我们扩展行为。
� 装饰者模式意味着一群装饰者类 , 这 些 类 用 来 包 装 具 体 组
件。
� 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型都经过接口或继承实现)。
� 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为 , 甚 至 将 被 装 饰 者 的 行 为整个取代掉,而达到特定的目的。
� 你可以用无数个装饰者包装一个组件。
� 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
� 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂

小练习

package designPatternExe.Decorator;


/*
*  1:米线有三种,干浆、酸浆和水米线。(以后也不好说)
 *  2. 配料有三种,豆腐、鸡蛋、牛肉,今后还会更多。
  *  3. 客户可疑随心所欲的要各种米线搭配各种配料,配料可以加同一种加多份,或者不同种加多份。
* */
public class dec_1 {
    public static void main(String[] args) {
        /*不断的装饰,一个类包装一个类,有点像在类
        中加入新的功能,把上个类的引用给当前类就好了,
        所以这也是为什么装饰类和被装饰类要类型一致,
        还能附加新的功能。
        * */
        //1:不需要配料的酸浆米线
        MiXian sjmx=new SuanJiang();
        System.out.println(sjmx.getDescription()+","+sjmx.cost());
        //2:来一份撒酸浆牛肉米线
        MiXian sjnrmx=new SuanJiang();
        sjnrmx=new Beef(sjnrmx,1);
        System.out.println(sjnrmx.getDescription()+","+sjnrmx.cost());
        //3:来一份酸浆牛肉鸡蛋米线
        MiXian sjnrjdmx=new SuanJiang();
        sjnrjdmx=new Beef(sjnrjdmx,1);
        sjnrjdmx=new Egg(sjnrjdmx,1);
        System.out.println(sjnrjdmx.getDescription()+","+sjnrjdmx.cost());
        //4:来一份酸浆牛肉米线 ,牛肉来两份
        MiXian sjnr2mx=new SuanJiang();
        sjnr2mx=new Beef(sjnr2mx,2);
        System.out.println(sjnr2mx.getDescription()+","+sjnr2mx.cost());
    }
}
//米线(被装饰者)
abstract  class  MiXian{
    String description="unkown mixian";

    public String getDescription() {
        return description;
    }

    abstract double  cost();
}
class GanJiang extends  MiXian{
    public GanJiang() {
        this.description="Ganjiang";
    }

    @Override
    double cost() {
        return 1.1;
    }
}
class SuanJiang extends MiXian{
    public SuanJiang() {
        this.description="SuanJIang";
    }

    @Override
    double cost() {
        return 2.2;
    }
}
class ShuiMiXian extends MiXian{
    public ShuiMiXian() {
        this.description="ShuiMixian";
    }

    @Override
    double cost() {
        return 3.3;
    }
}

//配料(装饰者)必须与被装饰者保持一样类型
//从而能取代被装饰者
abstract class  PeiLiao  extends MiXian{
    public abstract String getDescription();
}
class DouFu extends  PeiLiao{
    MiXian miXian;
    public int count=1;
    public DouFu() {
        this.description="DouFu";
    }

    public DouFu(MiXian miXian,int count) {
        this.description="DouFu";
        this.miXian = miXian;
        this.count=count;
    }

    @Override
    double cost() {
        return miXian.cost()+0.2*count;
    }

    @Override
    public String getDescription() {
        return miXian.getDescription()+","+this.description;
    }
}
class  Egg extends  PeiLiao{
    MiXian miXian;
    public  int count=1;
    public Egg(MiXian miXian,int count) {
        this.miXian = miXian;
        this.count=count;
        this.description="Egg";
    }

    public Egg() {
        this.description="Egg";
    }

    @Override
    double cost() {
        return miXian.cost()+0.3*count;
    }

    @Override
    public String getDescription() {
        return miXian.getDescription()+","+this.description;
    }
}
class  Beef extends  PeiLiao{
    public int count=1;
    MiXian miXian;
    public Beef() {
        this.description="Beef";
    }

    public Beef(MiXian miXian,int count) {
        this.miXian = miXian;
        this.count=count;
        this.description="Beef";
    }

    @Override
    double cost() {
        return miXian.cost()+0.5*count;
    }

    @Override
    public String getDescription() {
        return miXian.getDescription()+","+this.description;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mindcarver

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值