设计模式 之 装饰者模式

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的结果如下图:

技术分享

以上是关于设计模式 之 装饰者模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式

深入理解JavaScript系列(29):设计模式之装饰者模式

java设计模式之装饰者模式学习

php模式之装饰者模式学习

设计模式之装饰者模式

Java设计模式之装饰者模式