设计模式第四天之装饰者模式
在这次分享之前,我们先来分享一个设计原则------开放-关闭原则。其实在第一篇文章分享策略模式的时候已经使用过了。大家还记得那个android的图片缓存吗?大家发现,如果我要新增加一种缓存机制,只需要增加一个类(这个类实现ImageCache接口即可),然后然后通过setImageCache()方法将该类对象赋值给ImageLoader的ImageCache对象,就可以使用了。大家有没有发现?我在新增这个缓存机制的过程中只是新增了一个类,并没有对原有的代码进行修改,这就是遵循了开放-关闭原则。
开放-关闭原则,我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性,可以应对改变,可以接受新的功能类应对改变的需求。(引用《Head First设计模式》)。
我这次分享的装饰着模式是完全遵循开放-关闭原则的。
用《Head First设计模式》中的例子来描述吧。
需求是这样的?我们要为一个咖啡店设计一个订单系统。该咖啡店有几种(HouseBlend、DarkRoast、Decaf,Espresso)原汁原味的咖啡,都是原味的。然后顾客购买咖啡的时候,可以指定添加调料,调料有蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)和覆盖奶泡。
添加调料可以按照客户的要求添加多种,当然也可以不添加。然后计算出价钱(四种原味的咖啡要钱,调料也要钱)
大家可以想想你们的饿实现。下面我来分享一个装饰者模式的实现。
如果顾客想要摩卡和奶泡深焙咖啡。
- 拿一个深焙咖啡(DarkRoast)对象
- 以摩卡(Mocha)对象装饰它
- 以奶泡(Whip)对象装饰它
- 调用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对象即可。

浙公网安备 33010602011771号