设计模式第四天之装饰者模式

在这次分享之前,我们先来分享一个设计原则------开放-关闭原则。其实在第一篇文章分享策略模式的时候已经使用过了。大家还记得那个android的图片缓存吗?大家发现,如果我要新增加一种缓存机制,只需要增加一个类(这个类实现ImageCache接口即可),然后然后通过setImageCache()方法将该类对象赋值给ImageLoader的ImageCache对象,就可以使用了。大家有没有发现?我在新增这个缓存机制的过程中只是新增了一个类,并没有对原有的代码进行修改,这就是遵循了开放-关闭原则。

   开放-关闭原则,我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性,可以应对改变,可以接受新的功能类应对改变的需求。(引用《Head First设计模式》)。

   我这次分享的装饰着模式是完全遵循开放-关闭原则的。

用《Head First设计模式》中的例子来描述吧。

需求是这样的?我们要为一个咖啡店设计一个订单系统。该咖啡店有几种(HouseBlend、DarkRoast、Decaf,Espresso)原汁原味的咖啡,都是原味的。然后顾客购买咖啡的时候,可以指定添加调料,调料有蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)和覆盖奶泡。

添加调料可以按照客户的要求添加多种,当然也可以不添加。然后计算出价钱(四种原味的咖啡要钱,调料也要钱)

 大家可以想想你们的饿实现。下面我来分享一个装饰者模式的实现。

如果顾客想要摩卡和奶泡深焙咖啡。

  1. 拿一个深焙咖啡(DarkRoast)对象
  2. 以摩卡(Mocha)对象装饰它
  3. 以奶泡(Whip)对象装饰它
  4. 调用cost()方法,并依赖委托delegate将调料的价钱加上去。

《Head First设计模式》中的图。

首先画出UML建模图。

根据uml我们知道所有的主体或者装饰者都要间接或直接实现相同的接口,这也是装饰者模式所要求的。

首先来实现饮料的接口Bevarage,有最基本的获取描述和计算价钱的方法

 1 /**
 2  * 饮料和调料的公共的接口
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:31
 6  */
 7 public interface Beverage {
 8 
 9     public double cost();
10 
11     public String getDescription();
12 
13 }

基于饮料的接口,我们实现四种饮料

 1 /**
 2  * DarkRoast原味饮料
 3  * 该设计模式中为主体
 4  * @author wangpeiyu
 5  * @version 1.0
 6  * @created 06-11月-2016 10:41:38
 7  */
 8 public class DarkRoast implements Beverage {
 9 
10     private String des;
11     private double price;
12 
13     public DarkRoast(){
14         des="Dark Roase";
15         price=1.1;
16     }
17 
18     @Override
19     public double cost(){
20         return price;
21     }
22     @Override
23     public String getDescription(){
24         return des;
25     }
26 }
 1 /**
 2  *  * Decaf原味饮料
 3  * 该设计模式中为主体
 4  * @author wangpeiyu
 5  * @version 1.0
 6  * @created 06-11月-2016 10:41:39
 7  */
 8 public class Decaf implements Beverage {
 9 
10     private String des;
11     private double price;
12 
13     public Decaf(){
14         des="Decaf";
15         price=1.15;
16     }
17 
18     @Override
19     public double cost(){
20         return price;
21     }
22 
23     @Override
24     public String getDescription(){
25         return des;
26     }
27 }
 1 /**
 2  *  * Espresso原味饮料
 3  * 该设计模式中为主体
 4  * @author wangpeiyu
 5  * @version 1.0
 6  * @created 06-11月-2016 10:41:41
 7  */
 8 public class Espresso implements Beverage {
 9 
10     private String des;
11     private double price;
12 
13     public Espresso(){
14         des="Espresso";
15         price=1.2;
16     }
17     @Override
18     public double cost(){
19         return price;
20     }
21 
22     @Override
23     public String getDescription(){
24         return des;
25     }
26 }
 1 /**
 2  *HouseBlend原味饮料
 3  * 该设计模式中为主体
 4  * @author wangpeiyu
 5  * @version 1.0
 6  * @created 06-11月-2016 10:41:36
 7  */
 8 public class HouseBlend implements Beverage {
 9 
10     private String des;
11     private double price;
12 
13     public HouseBlend(){
14         des="HouseBlend";
15         price=1.3;
16     }
17 
18     @Override
19     public double cost(){
20         return price;
21     }
22 
23     @Override
24     public String getDescription(){
25         return des;
26     }
27 }

通过前面的两个图,我们知道调料作为装饰者,在类中要有一个Beverage饮料接口的一个对象,用来指向前面最新饮料的对象,这样在算钱的时候,可以先计算出前面的,然后在加上现在增加的。获取描述也是一样,先获取前面的描述再加上现在的描述。那首先我们来实现调料的抽象类。我们知道每个调料类都有Beverage接口指向前面的饮料,那我们可以直接在抽象类中写这个Beverage对象,然后在各自的调料类中赋值便可以了。(当然price价钱和des描述也是可以这样做的。)

调料类接口:

 1 /**
 2  * 调料的接口
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:42
 6  */
 7 public abstract class  CondimentDecorator implements Beverage {
 8 
 9     protected Beverage beverage;
10     @Override
11     public abstract double cost();
12     
13     @Override
14     public abstract String getDescription();
15 
16 }

下面是各种调料。

 1 /**
 2  * 调料
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:43
 6  */
 7 public class Milk extends CondimentDecorator {
 8 
 9     private String des;
10     private double price;
11 
12     /**
13      * 
14      * @param beverage
15      */
16     public Milk(Beverage beverage){
17         this.beverage = beverage;
18         des="Milk";
19         price = 0.5;
20     }
21     @Override
22     public double cost(){
23         return beverage.cost()+price;//前面的饮料的价钱,再加上当前增加的
24     }
25     @Override
26     public String getDescription(){
27         return beverage.getDescription()+" "+des;
28     }
29 }
 1 /**
 2  * 调料
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:44
 6  */
 7 public class Mocha extends CondimentDecorator {
 8 
 9     private String des;
10     private double price;
11     /**
12      * 
13      * @param beverage
14      */
15     public Mocha(Beverage beverage){
16         this.beverage = beverage;
17         des="Mocha";
18         price=0.6;
19     }
20 
21     @Override
22     public double cost(){
23         return beverage.cost()+price;
24     }
25     @Override
26     public String getDescription(){
27         return beverage.getDescription()+" "+des;
28     }
29 }
 1 /**
 2  * 调料
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:45
 6  */
 7 public class Soy extends CondimentDecorator {
 8 
 9     private String des;
10     private double price;
11     /**
12      * 
13      * @param beverage
14      */
15     public Soy(Beverage beverage){
16         this.beverage = beverage;
17         des="Soy";
18         price=0.4;
19     }
20 
21     @Override
22     public double cost(){
23         return beverage.cost()+price;
24     }
25     @Override
26     public String getDescription(){
27         return beverage.getDescription()+" "+des;
28     }
29 }
 1 /**
 2  * 调料
 3  * @author wangpeiyu
 4  * @version 1.0
 5  * @created 06-11月-2016 10:41:47
 6  */
 7 public class Whip extends CondimentDecorator {
 8 
 9     private String des;
10     private double price;
11     /**
12      * 
13      * @param beverage
14      */
15     public Whip(Beverage beverage){
16         this.beverage = beverage;
17         des="Whip";
18         price=0.45;
19     }
20 
21     @Override
22     public double cost(){
23         return beverage.cost()+price;
24     }
25     @Override
26     public String getDescription(){
27         return beverage.getDescription()+" "+des;
28     }
29 }

下面来写一个测试的例子。

 1 public class main {
 2     public static void main(String[] args) {
 3         // TODO Auto-generated method stub
 4         //新建一种饮料
 5         Decaf beverage = new Decaf();
 6         //原味的钱
 7         System.out.println(beverage.getDescription()+" $"+beverage.cost());
 8         //添加了牛奶调料,将主体传递进去
 9         Beverage beverage2 = new Milk(beverage);
10         //添加Mocha调料,这时候将前面的最新的饮料传递进去
11         beverage2 = new Mocha(beverage2);
12         //添加Whip饮料,又将最新的饮料传递进去
13         beverage2 = new Whip(beverage2);
14         //查看当前饮料的价钱
15         System.out.println(beverage2.getDescription()+" $"+beverage2.cost());
16     }
17 }

输出的结果是:

这样就实现了客户想加什么调料,直接新建一个给调料的类,并将之前的饮料传给调料里面的beverage对象即可。

posted @ 2016-11-19 21:58  于王令  阅读(76)  评论(0)    收藏  举报