设计模式 之 装饰者模式
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 之 装饰者模式相关的知识,希望对你有一定的参考价值。
装饰者模式,顾名思义,就是在一些固定不变的事物的基础之上,动态的添加一些其他的东西作为装饰,从代码的角度上说,装饰者模式就是在不修改任何底层代码的情况下,动态的给对象赋予新的职责,防止“类爆炸”。
设想这样一个例子:这是一个咖啡厅的结算系统,咖啡厅中有各种饮料,顾客点好自己要购买的饮料之后,服务员负责进行计算、结账。当然,顾客不一定要点现成的饮料,他们可以在原来饮料的基础上指定添加其他的佐料,比如双倍奶的浓缩咖啡、加豆浆的深培咖啡等。这要怎么办呢?
方法一:
最初始也最简单的想法是:对顾客的每一个选择,如果不重复,则新建一个类,让它继承饮料(Beverage)类。在原有饮料种类的基础上,顾客每点一种新的样式,就再多生成一个类,在类中标明该饮料所需的原料和价格。但是,这样一来就会发生“类爆炸”的现象:很多很多的饮料类聚集在项目中,不仅拖慢了项目的运行速度,而且维护起来极度困难。
方法二:
第二种方法是:在饮料父类中加入一些判定是否加入某种原料的方法,如是否加豆浆、是否加奶的方法,这样一来,就不会再发生“类爆炸”的现象,相反的,我们仅仅需要几个类就可以完成。当顾客点了一杯饮料之后,售货员将顾客所点的原料的“需要”属性设置为true,最后根据每种原料的“需要”属性是否为true计算总价。但是,这样做也不能达到预期的效果。试想,如果要新加入一种原料,那么我们除了要增加一个原料的类之外,还要在饮料类中加入一个设置该原料“需要”属性的方法,这样违反了代码的可维护性;另一方面,如果顾客点了“双倍奶”这个选项,那么我们怎样标记这“双份”?也就是说,用第二种方法,虽然解决了“类爆炸”问题,但代码还是不够灵活。
方法三:
方法三就是使用装饰者模式来解决这个问题。装饰者模式是动态的将责任附加到对象上。装饰者可以在被装饰者的行为之前/之后,加上自己的行为,以达到特定的目的。简单的来说,装饰者模式就是在最开始的对象上面,一层一层的包裹上装饰物,当需要获取装饰物的某个总属性时,则递归的获取每个装饰物的这个属性,直到递归到最开始的对象。这样,不仅解决了“类爆炸”的问题,还提高了代码的灵活性:既不需要再修改底层的代码,也可以添加像“双倍奶”这种有数量要求的装饰物——只需要包裹上两层“奶”这个装饰物即可。
下面贴出“咖啡厅”问题的代码。要完成这个程序,我们需要两种类:一种是“底层”的饮料,如浓缩咖啡、烘焙咖啡等,作为饮料的“底料”;另一种类是装饰物类,比如奶、豆浆等,作为“调料”。在使用装饰者模式时调和饮料时,只能有一种“底料”,可以有多种“调料”一层一层的包裹在“底料”外围。因此,我们需要一个饮料的总父类Beverage、各个“底料”的类(下面的代码中以浓缩咖啡Espresso为例)和各个“调料”类(下面的代码中以奶Milk为例)。
饮料的总父类Beverage类的代码如下:
1 public abstract class Beverage { 2 protected String description; 3 4 public String getDescription() { 5 return this.description; 6 } 7 8 public abstract double getCost(); 9 }
“底料”浓缩咖啡Espresso类的代码如下:
1 public class Espresso extends Beverage { 2 3 public Espresso() { 4 super.description = "Espresso"; 5 } 6 7 public double getCost() { 8 return 0.89; 9 } 10 }
“调料”奶Milk类的代码如下:
1 public class Milk extends Beverage { 2 private Beverage beverage; 3 4 public Milk(Beverage beverage) { 5 this.beverage = beverage; 6 } 7 8 public String getDescription() { 9 return beverage.getDescription() + ",Milk"; 10 } 11 12 public double getCost() { 13 return 0.20 + beverage.getCost(); 14 } 15 }
测试类MainClass类的代码如下:
1 public class MainClass { 2 public static void main(String[] args) { 3 Beverage beverage1 = new DarkRoast(); 4 System.out.println(getBeverageInfo(beverage1)); 5 6 Beverage beverage2 = new Espresso(); 7 beverage2 = new Milk(beverage2); 8 beverage2 = new Milk(beverage2); 9 beverage2 = new Mocka(beverage2); 10 System.out.println(getBeverageInfo(beverage2)); 11 12 Beverage beverage3 = new HouseBlend(); 13 beverage3 = new Soy(beverage3); 14 beverage3 = new Whip(beverage3); 15 beverage3 = new Mocka(beverage3); 16 beverage3 = new Mocka(beverage3); 17 beverage3 = new Mocka(beverage3); 18 System.out.println(getBeverageInfo(beverage3)); 19 } 20 21 private static String getBeverageInfo(Beverage beverage) { 22 return beverage.getDescription() + " 共花费$" + beverage.getCost(); 23 } 24 }
运行MainClass的结果如下图:
以上是关于设计模式 之 装饰者模式的主要内容,如果未能解决你的问题,请参考以下文章