headFirst设计模式——装饰者模式

一旦你熟悉了装饰的技巧,你将能够不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。

一、引入

  星巴兹咖啡因为扩张速度太快了,他们准备更新订单系统,以合乎他们的饮料供应需求。

  原先的设计类:

  headFirst设计模式——装饰者模式

  购买咖啡时,可以加入各种调味。星巴兹会根据加入的调料收取不同的费用。

  headFirst设计模式——装饰者模式

   违反设计原则:封装变化;多用组合,少用继承;

  改法一:应用实例变量和继承。

    1、在基类上加上调料实例,并在cost()方法中实现基本逻辑,让子类继承。

    2、子类可以在其基础价格上加上超类价格(各种调料的价格)。

  思考:一旦出现新的调料,我们要修改基类cost方法。

      出现新的饮品,某些调料可能并不适合。

      万一顾客想要双倍奶泡呢。

  利用组合的方式,可以把设计超类时没有想到的职责加在对象上。而且,可以不用修改原来的代码。

  设计原则:

    类应该对扩展开放,对修改关闭。

    允许类容易扩展,在不修改先有代码的情况下,就可搭配新的行为。(在需要的地方遵循 开放-关闭原则,过度的使用这个原则也是一种浪费,还会导致代码变得复杂且难以理解)。

二、装饰者模式

  以饮料为主题,然后在运行时以调料来“装饰”(decorate)饮料。

  比如说用户想要摩卡和奶泡深焙咖啡,那么,要做的是:

  1、拿一个深焙咖啡(DarkRoast)的对象

  2、以摩卡(Mocha)对象装饰它

  3、以奶泡(whip)对象装饰它

  4、调用cost()方法,并依赖委托(delegate)将调料的价钱加上去

  headFirst设计模式——装饰者模式

   headFirst设计模式——装饰者模式

  小结:

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

    2、你可以用一个或多个装饰者包装一个对象。

    3、既然装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。

    4、装饰者可以在所委托被装饰者之前/或之后,加上自己的行为,以达到特定的目的

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

  装饰者模式:

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

  headFirst设计模式——装饰者模式

  星巴兹咖啡

  headFirst设计模式——装饰者模式

抽象组件

 1 // Beverage 是一个抽象类
 2 public abstract class Beverage {
 3 
 4     String description = "Unknown Beverage";
 5     
 6     // 对饮品的描述
 7     public String getDescription() {
 8         return description;
 9     }
10     
11     // cost要在子类中实现(都要实现)
12     public abstract double cost();
13 }

headFirst设计模式——装饰者模式

 

 

抽象装饰者——调料Condiment抽象类

1 public abstract class CondimentDecorator extends Beverage {
2 
3     public abstract String getDescription();
4 
5 }

具体组件——饮品

 1 public class Espresso extends Beverage {
 2 
 3     public Espresso() {
 4         description = "Espresso";
 5     }
 6 
 7     @Override
 8     public double cost() {
 9         return 2.99;
10     }
11 
12 }

具体装饰者——调料

 1 public class Milk extends CondimentDecorator {
 2 
 3     Beverage beverage;// 用一个实例变量记录饮料,也就是被装饰者
 4     
 5     public Milk(Beverage beverage) {
 6         this.beverage = beverage;// 让被装饰者被记录到实例变量中。把饮料当做构造器参数,再由构造器将此饮料记录在实例变量中
 7     }
 8 
 9     @Override
10     public String getDescription() {
11         // 我们希望描述的不止是饮料,而是完整地连调料都描述出来。
12         return beverage.getDescription()+",Milk";// 我们利用委托的做法,得到一个叙述,然后再加上其他的附加描述。
13     }
14 
15     @Override
16     public double cost() {
17         return beverage.cost() + 0.5;// 调用委托给被装饰对象,以计算价钱,然后再加上调料的价钱。
18     }
19 
20 }

测试

 1 public class StarbuzzCoffee {
 2 
 3     public static void main(String[] args) {
 4         Beverage beverage = new Espresso();
 5         System.out.println(beverage.getDescription()+" $"+beverage.cost());//Espresso $2.99
 6         
 7         Beverage darkRoast = new DarkRoast();
 8         Beverage darkMocha = new Mocha(darkRoast);
 9         Beverage dark2Mocha = new Mocha(darkMocha);
10         Beverage dark2MochaWhip = new Whip(dark2Mocha);
11         System.out.println(dark2MochaWhip.getDescription()+" $"+dark2MochaWhip.cost());
12 //        DarkRoast,Mocha,Mocha,Whip $4.7299999999999995
13         
14         Beverage houseBlend = new HouseBlend();
15         houseBlend = new Soy(houseBlend);
16         houseBlend = new Mocha(houseBlend);
17         houseBlend = new Whip(houseBlend);
18         System.out.println(houseBlend.getDescription() + " $"+houseBlend.cost());
19 //        HouseBlend,Soy,Mocha,Whip $3.2300000000000004
20         
21     }
22     
23 }

  思考:

    1、如果碰到打折呢

    2、如果按大中小杯收费

    3、想要展示Double Mocha 而不是Mocha,Mocha

  优点:

    为设计注入弹性

    透明地插入装饰者,客户程序甚至不需要知道它是在和装饰者打交道

  问题:

    不易理解(要明确这些小类(调料)都是用来包装某个类(饮料)的)

    插入装饰者过多,容易混淆(插入装饰者时要谨慎)

    采用装饰者在实例化组件时,增加代码难度。

    一旦使用装饰者模式,不只需要实例化组件,还要把次组件包装进装饰者中。

  解决:

    工厂(Factory)模式和生成器(Builder)模式,隐藏代码难度。

  

  设计原则:

    1、封装变化

    2、多用组合,少用继承

    3、针对接口编程,不针对实现编程

    4、为交互对象之间的松耦合设计而努力

    5、对扩展开放,对修改关闭

  

  『装饰者模式』动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。

  

  继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。

  在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。

  组合和委托可用于在运动时动态地加上新的行为。

  除了继承,装饰者模式也可以让我们扩展行为。

  装饰者模式意味着一群装饰者类,这些类用来包装具体组件。

  装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。

  装饰者可以在被装饰者的行为前面/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。

  你可以用无数个装饰者包装一个组件。

  装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。

  装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变的很复杂。

 

上一篇:Headfirst 设计模式 章一


下一篇:Airtest API精讲之放大缩小pinch()