设计模式--Decorator

本文参考 Head First 设计模式,如果觉得四人帮的书晦涩难懂,强烈建议看这一本,嘿嘿。

在实际设计过程中,我们应遵循开闭原则–类应该对扩展开放,对修改关闭。这尤其体现在后期的维护过程中,我们希望我们能对已有的功能进行扩展,而已经通过测试的功能代码不用做任何改变。这样我们就可以尽量避免因为添加新的功能而修改原始代码而引入不必要的bug。

Decorator模式便是一种良好遵循开闭原则的设计模式。

在描述Decorator模式之前,我们先看一个例子,我们将由这个例子引出Decorator模式

现在有一个需求是为一家咖啡馆设计一个订单系统,这个订单系统非常简单,我们只需要在顾客选择咖啡种类后为其提供价格。我们这里假设有两种咖啡HouseBlend和Decaf,四种加料milk,soy,moca,whip。

首先我们定义一个咖啡抽象类

public abstract class Beverage {
    private String description;

    public String getDescription() {
        return this.description;
    }
    public void setDescription(String description){
        this.description = description;
    }

    public abstract Integer cost();
}

之后假设我们需要一个加了milk和moca的HouseBlend,以及一个加了soy和whip的Decaf。有一种实现方式是

public class HouseBlendWithMilkAndMocka extends Beverage{
    @Override
    public Integer cost() {
       return 15;
    }
}
public class DecafWithSoyAndWhip extends Beverage{

    @Override
    public Integer cost() {
        return 17;
    }

}

显然,这是最糟糕的一种设计,糟糕在哪里不必多言。

为了防止上面方式导致的类爆炸,以及结构丑陋。我们决定更改为以下设计:
将各种加料以bool值变量放到Beverage抽象类中,并且Beverage中的cost方法将会提供计算调料价格的功能,像这样


public abstract class Beverage {
    private String description;

    private Boolean milk;
    private Boolean soy;
    private Boolean mocha;
    private Boolean whip;

    //getter and setter...

    public Integer cost() {
        Integer cost = 0;
        if (this.getMilk())cost += 2;
        if (this.getMocha())cost += 3;
        //...
        return cost;
    }
}

这时候HouseBlend的实现如下,


public class HouseBlend extends Beverage {
    public Integer cost() {
        return 10 + super.cost();
    }
}

在实际使用中,为了计算加了milk和mocha的咖啡的价格我们可以这样做:


public class Test {
   public static void main(String[] args){
       Beverage houseBlendWithMilkAndMocha = new HouseBlend();
       houseBlendWithMilkAndMocha.setMilk(true);
       houseBlendWithMilkAndMocha.setMocha(true);
       System.out.println(houseBlendWithMilkAndMocha.cost());
   }
}

显然,现在的设计已经有了长足的进步,我们只需要设计三个类就可以获得加了任意一种调料的咖啡的价格。

但是,这里的实现是在咖啡加料种类固定的情况下。如果咖啡馆突然进了一种新的加料,那么我们就需要修改Beverage类,显然这是不满足开闭原则的。再者,如果消费者想要加了双份mocha的咖啡改如何是好。

所以以上的设计仍然存在很大的问题。

那么现在我们需要一种更加灵活的设计方式,考虑以下方式,
首先我们还是定义一个这样的超类:

public abstract class Beverage {
    private String description;

    public String getDescription() {
        return this.description;
    }
    public void setDescription(String description){
        this.description = description;
    }

    public abstract Integer cost();
}

然后是我们的HouseBlend类

public class HouseBlend extends Beverage {

    @Override
    public Integer cost() {
        return 10;
    }
}

之后我们将各种调料也定义成Beverage的子类,并且持有一个Beverage的对象,像这样:


public class Milk  extends Beverage{
    private Beverage beverage;

    public Milk(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public Integer cost() {
        return beverage.cost() + 3;
    }

}
public class Mocha extends Beverage {
    private Beverage beverage;

    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public Integer cost() {
        return beverage.cost() + 3;
    }

}

...

这样,为了获得加了milk和mocha的HouseBlend咖啡,我们可以这样实现:

public class Test {
   public static void main(String[] args){
      HouseBlend blend = new HouseBlend();
      Beverage blendWithmilk = new Milk(blend);
      Beverage blendWithMilkAndMocha = new Mocha(blendWithmilk);

      System.out.println(blendWithMilkAndMocha.cost());
   }
}

由于加料类型和咖啡类型都是Beverage的子类,加料类型中的Beverage属性可以是任意的一个原始的饮料类型或者已经加料的类型。并且可以进行重复的添加。这便是Decorator模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值