《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 设计模式》之装饰者模式的主要内容,如果未能解决你的问题,请参考以下文章