《Head First 设计模式》之装饰者模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Head First 设计模式》之装饰者模式相关的知识,希望对你有一定的参考价值。

前言:

  时间过得挺快的,转眼间就到了十月中旬了,再晃着晃着新的一年就要来临。今天lz有幸来到了浙大图书馆来学习,想想自己已经毕业两年了,今日再次踏进校园里,心里颇有一丝感悟,说不出来,只有把它放在心里。lz前段时间看到一篇文章,觉得里面有句话说的很好,想拿出来与大家分享。文章的标题是《在写作中成长》,有句话是说,我们可以通过写作来学习,学习方法是每个输入都尽量有所输出。 无论是读书,看文章,参加技术讲座,还是看电影,尽量或多或少的总结输出出来,输出形式要么是一篇博客,要么是一篇微博,要么是一篇笔记。因为自己完全掌握的知识应该是自己能表达出来的知识。

今日分享:

  老样子,今天给大家带来两句话。

  1.很喜欢这句话:你现在的气质里,藏着你走过的路,读过的书和爱过的人。

  2.一句英文,The only you in the world, ever if no one appreciate, should love and take care of yourself.世界上唯一的你,就算没有人欣赏,也要好好爱自己。真正的美就是做自己,你不需要被大部分人认可,你只需要接受最真实的自我!

装饰者模式

  今天给大家带来的是装饰者模式。一说到复用我想大家脑子里的第一反应应该就是继承,通过对这个模式的学习,大家可以领悟到运行时期的扩展远比编译时期的继承威力大。也即,我们可以采取对象组合的方式,做到在运行时装饰类。貌似说的有点抽象哈,OK,下面继续和大家探讨装饰者模式。

  咖啡实例:

  现给出一个具体的实例让大家初步感受下装饰者模式。某家咖啡连锁店里面有多种饮料供应,每种饮料也可以要求在其中加入各种调料,这家连锁店会根据所加入的调料收取不同的费用。先画出该案例的UML类图:

  UML:

  技术分享

下面给出具体代码示例:

先给出被装饰者角色的抽象类:

 1 package xin.yangmj.decorate.decorated;
 2 
 3 /**
 4  * 这是饮料抽象类,角色相当于被装饰者
 5  *
 6  * @author Eric Yang
 7  * @create 2017-10-14 下午2:24
 8  **/
 9 public abstract class Beverage {
10 
11     public String description = "Unknown Beverage";
12 
13     public String getDescription() {
14         return description;
15     }
16 
17     public abstract double cost();
18 }

再给出四个具体的饮料实现类:

 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 一种具体的饮料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class DarkRoast extends Beverage {
12 
13     public DarkRoast() {
14         description = "Dark Roast Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 2.99;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 一种具体的饮料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class Decaf extends Beverage {
12 
13     public Decaf() {
14         description = "Decaf Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 2.09;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 这是浓缩咖啡,一种具体的饮料
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:30
10  **/
11 public class Espresso extends Beverage {
12 
13     public Espresso() {
14         description = "Espresso Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 1.99;
20     }
21 }
 1 package xin.yangmj.decorate.decorated.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 另一种饮料的实现
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:34
10  **/
11 public class HouseBlend extends Beverage {
12 
13     public HouseBlend() {
14         description = "House Blend Coffee";
15     }
16 
17     @Override
18     public double cost() {
19         return 0.89;
20     }
21 }

再给出抽象调料装饰者,为装饰者角色:

 1 package xin.yangmj.decorate.decorator;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 
 5 /**
 6  * 这是调料抽象类,角色相当于装饰者
 7  *
 8  * @author Eric Yang
 9  * @create 2017-10-14 下午2:28
10  **/
11 public abstract class CondimentDecorator extends Beverage {
12 
13     public abstract String getDescription();
14 }

再给出四个具体的调料装饰者实现类:

 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 这是调料的一种实现
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Milk extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Milk(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是该调料装饰者饮料,这也解释了
22      * 为什么调料装饰者要有个抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Milk";
29     }
30 
31     /**
32      * 这是总价钱,包含包装的饮料和此调料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.30 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 这是调料的一种实现
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:35
11  **/
12 public class Mocha extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Mocha(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是该调料装饰者饮料,这也解释了
22      * 为什么调料装饰者要有个抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Mocha";
29     }
30 
31     /**
32      * 这是总价钱,包含包装的饮料和此调料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.20 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 这是调料的一种实现
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Soy extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Soy(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是该调料装饰者饮料,这也解释了
22      * 为什么调料装饰者要有个抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Soy";
29     }
30 
31     /**
32      * 这是总价钱,包含包装的饮料和此调料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.15 + beverage.cost();
39     }
40 }
 1 package xin.yangmj.decorate.decorator.impl;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorator.CondimentDecorator;
 5 
 6 /**
 7  * 这是调料的一种实现
 8  *
 9  * @author Eric Yang
10  * @create 2017-10-14 下午2:54
11  **/
12 public class Whip extends CondimentDecorator {
13 
14     Beverage beverage;
15 
16     public Whip(Beverage beverage) {
17         this.beverage = beverage;
18     }
19 
20     /**
21      * 表明是该调料装饰者饮料,这也解释了
22      * 为什么调料装饰者要有个抽象的getDescription()方法
23      *
24      * @return
25      */
26     @Override
27     public String getDescription() {
28         return beverage.getDescription() + ", Whip";
29     }
30 
31     /**
32      * 这是总价钱,包含包装的饮料和此调料
33      *
34      * @return
35      */
36     @Override
37     public double cost() {
38         return 0.18 + beverage.cost();
39     }
40 }

最后给出客户端测试代码:

 1 package xin.yangmj.decorate;
 2 
 3 import xin.yangmj.decorate.decorated.Beverage;
 4 import xin.yangmj.decorate.decorated.impl.DarkRoast;
 5 import xin.yangmj.decorate.decorated.impl.Espresso;
 6 import xin.yangmj.decorate.decorated.impl.HouseBlend;
 7 import xin.yangmj.decorate.decorator.impl.Mocha;
 8 import xin.yangmj.decorate.decorator.impl.Soy;
 9 import xin.yangmj.decorate.decorator.impl.Whip;
10 
11 /**
12  * 这是客户端,用来测试供应咖啡
13  *
14  * @author Eric Yang
15  * @create 2017-10-14 下午2:50
16  **/
17 public class StarbuzzCoffee {
18     public static void main(String[] args){
19         Beverage beverage = new Espresso();
20         // 没有加任何调料
21         System.out.println(beverage.getDescription() + "--- $" + beverage.cost());
22 
23         // 用两个Mocha装饰它,然后再用Whip装饰
24         Beverage beverage2 = new DarkRoast();
25         beverage2 = new Mocha(beverage2);
26         beverage2 = new Mocha(beverage2);
27         beverage2 = new Whip(beverage2);
28         System.out.println(beverage2.getDescription() + "--- $" + beverage2.cost());
29 
30         // 分别用Soy, Mocha, Whip来装饰
31         Beverage beverage3 = new HouseBlend();
32         beverage3 = new Soy(beverage3);
33         beverage3 = new Mocha(beverage3);
34         beverage3 = new Whip(beverage3);
35         System.out.println(beverage3.getDescription() + "--- $" + beverage3.cost());
36     }
37 }

运行结果如下:

技术分享

  认识装饰者模式

  通过上面的例子,我们可以以饮料为主体,然后在运行时以调料来“装饰”(decorate)饮料。比如,顾客想要摩卡和奶泡深焙咖啡,那么,要做的是:

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

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

  3.以奶泡(Whip)对象装饰它

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

    定义装饰者模式

    官方定义:装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。具体UML类图如下:

  UML类图

技术分享

通过上面的定义和UML类图,可以得出关于装饰者模式的以下几点结论:

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

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

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

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

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

    满足的设计原则

  开放-关闭原则:类应该对扩展开发,对修改关闭。可以理解为,允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样子的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。具体在本例中,我们可以任意增加饮料和调料的种类,方便此咖啡店对它们的产品进行扩展,并且无需对现有的代码进行改变。只要新增加的种类满足现有的规范即可,也即:实现装饰者和被装饰者抽象类即可!

  答惑:在此特别和大家说明一点,装饰者模式最重要的一点是:装饰者需要和被装饰者(被包装的组件)有相同的“接口”,因为,装饰者必须能取代被装饰者。也即:这两者必须是一样的类型,具有共同的超类。在这里,我们利用继承达到“类型匹配”,而不是利用继承获得“行为”。行为来自装饰者和基础组件,或与其他装饰者之间的组合关系。

  真实世界的装饰者:Java I/O

  此处有待后续补充...

  

  

  

  

 

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

Head First设计模式之装饰者模式(Decorator Pattern)

《Head First 设计模式》之装饰者模式

Head First设计模式——装饰者模式

《Head First 设计模式》之代理模式

Head First设计模式 装饰者模式

Head First Design Patten装饰者模式