JAVA设计模式之装饰者模式
咖啡店需要做一个订单系统,以合乎饮料供应要求。
1.最初是这样设计的:
1 /** 2 * 饮料抽象类 3 * 4 */ 5 public abstract class Beverage { 6 7 protected String description; 8 9 public String getDescription() { 10 return this.description; 11 } 12 13 /** 14 * 子类需自定义自己的价格 15 * @return 16 */ 17 public abstract double cost(); 18 19 }
每一种饮料都需要继承该抽象类,并覆写cost()方法。
2.但是购买咖啡时需要考虑到调料的部分,每种咖啡会加不同种的调料,比如蒸奶、豆浆、摩卡或者覆盖奶泡,那么订单系统需要考虑加入不同调料后的价格。因此需要实现不同的子类来定义添加不同调料后的价格。大家知道,一种咖啡跟多种调料有多种组合方式,那么多种咖啡和多种调料的组合后,几乎是类爆炸!
维护极其困难:
如果某种饮料价格调整;或是新增了某种饮料,怎么办?
后来经过改进后,把是否存在某种调料作为饮料的属性,并在饮料抽象类中实现cost方法,子类可以覆写cost方法并设置添加的调料最终确定添加不同调料后的价格:
1 /** 2 * 饮料抽象类 3 * 4 */ 5 public abstract class Beverage { 6 7 private boolean milk;//牛奶 8 private boolean soy;//豆浆 9 private boolean mocha;//摩卡 10 private boolean whip;//奶泡 11 12 private double milkCost = 0.19; 13 private double soyCost = 0.26; 14 private double mochaCost = 0.29; 15 private double whipCost = 0.17; 16 17 protected String description; 18 19 //setter getter method 20 21 public String getDescription() { 22 return this.description; 23 } 24 25 public double cost() { 26 double condimentCost = 0.0; 27 if (hasMilk()) { 28 condimentCost += milkCost; 29 } 30 if (hasSoy()) { 31 condimentCost += soyCost; 32 } 33 if (hasMocha()) { 34 condimentCost += mochaCost; 35 } 36 if (hasWhip()) { 37 condimentCost += whipCost; 38 } 39 return condimentCost; 40 } 41 42 } 43 44 /** 45 * 低糖咖啡 46 * 47 */ 48 public class Decaf extends Beverage { 49 50 @Override 51 public String getDescription() { 52 return "It is Decaf."; 53 } 54 55 @Override 56 public double cost() { 57 super.setMilk(true);//添加牛奶调料 58 return 1.99 + super.cost(); 59 } 60 61 }
这样一来,如果有五种咖啡,那么只需要实现五个子类即可,不同的子类可以灵活设置添加不同的调料。
但是这样的设计存在一定的问题:
1)调料价格的改变会使我们改变现有代码;
2)出现新调料,就需要加上新的方法,并改变父类中的cost方法;
3)若出现新的饮料,如红茶,那么新的饮料继承该父类,父类中的调料属性并不合适,如奶泡等;
... ...
设计原则:类应该对扩展开放,对修改关闭。
装饰者模式思想:以饮料为主体,然后在运行时以调料来“装饰”饮料。
例如客户需要摩卡和奶泡深焙咖啡,那么要做的是:
拿一个深焙咖啡对象;
以摩卡对象装饰;
以奶泡对象装饰;
摩卡和奶泡属于调料,但是也是装饰者,它的类型反映了它装饰的对象,所谓反映,指的是两者类型一致。那么所有调料需要继承Beverage。
使用装饰者模式设计的代码:
1 /** 2 * 饮料抽象类 3 * 4 */ 5 public abstract class Beverage { 6 7 protected String description; 8 9 public String getDescription() { 10 return this.description; 11 } 12 13 /** 14 * 获取每种饮料的价格 15 * @return 16 */ 17 public abstract double cost(); 18 } 19 20 /** 21 * 调料抽象类 22 * 23 */ 24 public abstract class Condiment extends Beverage { 25 26 public abstract String getDescription(); 27 28 }
这里调料继承饮料,仅仅是为了使两者具有相同的类型,并非为了复用父类的行为。
下面是饮料的子类:
1 /** 2 * 深焙咖啡 3 * 4 */ 5 public class DarkRoast extends Beverage { 6 7 public DarkRoast() { 8 description = "DarkRoast"; 9 } 10 @Override 11 public double cost() { 12 return 0.19; 13 } 14 15 } 16 17 /** 18 * 浓缩咖啡 19 * 20 */ 21 public class Espresso extends Beverage { 22 23 public Espresso() { 24 description = "Espresso"; 25 } 26 27 @Override 28 public double cost() { 29 return 1.99; 30 } 31 32 } 33 34 /** 35 * 黑咖啡 36 * 37 */ 38 public class HoseBlend extends Beverage { 39 40 public HoseBlend() { 41 description = "Hose Blend Coffee"; 42 } 43 44 @Override 45 public double cost() { 46 return 0.99; 47 } 48 49 }
调料(装饰者)子类:
1 /** 2 * 摩卡 3 * 4 */ 5 public class Mocha extends Condiment { 6 7 private Beverage beverage; 8 9 public Mocha(Beverage beverage) { 10 this.beverage = beverage; 11 } 12 13 @Override 14 public String getDescription() { 15 return beverage.getDescription() + ", Mocha"; 16 } 17 18 @Override 19 public double cost() { 20 return 0.20 + beverage.cost(); 21 } 22 23 } 24 25 /** 26 * 豆浆 27 * 28 */ 29 public class Soy extends Condiment { 30 31 private Beverage beverage; 32 33 public Soy(Beverage beverage) { 34 this.beverage = beverage; 35 } 36 37 @Override 38 public String getDescription() { 39 return beverage.getDescription() + ", Soy"; 40 } 41 42 @Override 43 public double cost() { 44 return 0.23 + beverage.cost(); 45 } 46 47 } 48 49 /** 50 * 奶泡 51 * 52 */ 53 public class Whip extends Condiment { 54 55 private Beverage beverage; 56 57 public Whip(Beverage beverage) { 58 this.beverage = beverage; 59 } 60 61 @Override 62 public String getDescription() { 63 return beverage.getDescription() + ", Whip"; 64 } 65 66 @Override 67 public double cost() { 68 return 0.69 + beverage.cost(); 69 } 70 71 }
测试代码:
1 public class ComponentTest { 2 @Test 3 public void test() { 4 Beverage beverage = new Espresso(); 5 System.out.println(beverage.getDescription() + ", $" + beverage.cost()); 6 Beverage beverage2 = new HoseBlend(); 7 beverage2 = new Mocha(beverage2); 8 beverage2 = new Mocha(beverage2); 9 beverage2 = new Whip(beverage2); 10 System.out.println(beverage2.getDescription() + ", $" + beverage2.cost()); 11 Beverage beverage3 = new DarkRoast(); 12 beverage3 = new Soy(beverage3); 13 beverage3 = new Mocha(beverage3); 14 beverage3 = new Whip(beverage3); 15 System.out.println(beverage3.getDescription() + ", $" + beverage3.cost()); 16 } 17 }
运行结果:
1 Espresso, $1.99 2 Hose Blend Coffee, Mocha, Mocha, Whip, $2.08 3 DarkRoast, Soy, Mocha, Whip, $1.31
java/IO中有很多用到装饰者模式的设计,有兴趣的朋友可以了解下。