调侃《HeadFirst设计模式》之装饰者模式

      今天让我们继续跟随《Head First 设计模式》的足迹,聊一个新的设计模式,有了它,你将能够在不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。首先,按照惯例,从故事说起——

    星巴兹(Starbuzz)是以扩张速度最快而闻名的咖啡连锁店。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的……

    调侃《HeadFirst设计模式》之装饰者模式_第1张图片

  cost方法是计算咖啡价格的方法,description是描述咖啡的方法,比如描述咖啡加了什么调料。乍一看设计也是ok的,但是调料的增多,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。而且调料的价格的不同,就会出现一种情况,所谓类爆炸!

     调侃《HeadFirst设计模式》之装饰者模式_第2张图片

    一种配置的咖啡一个类,对于星巴克这种级别的咖啡馆来说是令人奔溃的设计,而且增加调料种类还会修改起来让人疯掉。

    其实完全没必要如此设计,可以在基类Beverage增加一些判断所添加的调料种类的布尔类型的成员变量和各种调料的价格就好了,然后在不同的咖啡的实现类中的cost根据那些判断的成员变量就可以计算出总的价格了。

     设计如下:


             调侃《HeadFirst设计模式》之装饰者模式_第3张图片

      这样只需要五个子类(五种饮料)就可以解决战斗了,不过怎么看都觉得别扭。

    1.调料价钱的改变会使我们更改现有代码。
    2.一旦出现新的调料,我们就需要加上新的方法,并改变超类中的cost()方法。
    3.以后可能会开发出新饮料。对这些饮料而言(例如:冰茶),某些调料可能并不适合,但是在这个设计方式中,Tea(茶)子类仍将继承那些不适合的方法,例如:hasWhip()(加奶泡)。万一顾客想要双倍摩卡咖啡,怎么办?

    还是那句话,这样的设计扩展性差。

   于是你又打电话给了深山的大师,大师又抛出一句话:“牢记,代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一样地开放(能够扩展)。”其实大师想说的是一个设计原则:类应该对扩展开放,对修改关闭。(所谓“开放-关闭”原则)
     我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。 

    这听起来有点不可思议,你很迷茫,正想再打电话给大师的时候,发现大师已经发了条短信给你:“可以使用装饰着模式”。

   什么事装饰者模式呢?比较官方的说法如下:

   装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

   模式的框架图大致如下:

   调侃《HeadFirst设计模式》之装饰者模式_第4张图片

       该模式的特点是:

    1.装饰者和被装饰对象有相同的超类型。

    2.你可以用一个或多个装饰者包装一个对象。
    3. 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,
可以用装饰过的对象代替它。
    4.装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。

    5.对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰
对象。

    6.具体应用于我的咖啡系统。在这里要采用不一样的做法:我们要以饮料为主体,然后在运行时以调料来“装饰”(decorate)饮料。


   如何做到“用调料来装饰咖啡”呢?大致思想类似下图:

   调侃《HeadFirst设计模式》之装饰者模式_第5张图片

    纯咖啡是中间的核心,每次加一种调料,就在外层加一层包装。

   代码结构大致如下:

   调侃《HeadFirst设计模式》之装饰者模式_第6张图片

     这里也运用到了继承,但是这么做的重点在于,装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,我们利用继承达到“类型匹配”,而不是利用继承获得“行为”。

      想必各位听得仍然云里雾里的,来,上代码自然一切就清楚了:

      咖啡基类Beverage:

      调侃《HeadFirst设计模式》之装饰者模式_第7张图片

     调料装饰者基类:

     

    必须继承Beverage,才完成装饰的功能。

    两个咖啡实例:

    咖啡Espresso:

    调侃《HeadFirst设计模式》之装饰者模式_第8张图片

   咖啡HouseBlend:

   调侃《HeadFirst设计模式》之装饰者模式_第9张图片

    调料,比如Mocha:

    调侃《HeadFirst设计模式》之装饰者模式_第10张图片

      看到这里,各位应该知道是如何装饰了吧。其实很简单,就是通过构造器获得被装饰者的委托,然后在getDescription和cost方法获取被装饰者的描述和价格接着将其和本类对应的调料的描述和价格用类似拼接的方式合并在一起。

      激动人心的时刻到了,进入运行代码了:

     调侃《HeadFirst设计模式》之装饰者模式_第11张图片

      运行结果:

     调侃《HeadFirst设计模式》之装饰者模式_第12张图片

      看,只要在运行时代码通过new对象传入适当的参数就可以任意创建不同调料组合的咖啡了。类的数量不大,增加咖啡或者调料只要增加相应的类即可,无需修改其他的类,价格的改变也只需在相应的类稍微修改下即可。是不是很爽?

     装饰者模式在实际应用中最典型的例子就是java.io包的类。刚学java io的人一定会被io包中的类数量之庞大、功能之类似搞的一头雾水把。但是当我们知道了装饰者模式后,对于io包的理解就会简单清晰多了,因为Io包的类就是不断使用装饰者模式,一个一个类的装饰,为被装饰者添加新的功能,使用者根据需要去使用对应的类。

    以下是一个简单的说明图:

    调侃《HeadFirst设计模式》之装饰者模式_第13张图片

    祝各位学习快乐。

你可能感兴趣的:(设计模式,面向对象)