明天来介绍装璜者模式(_Decorator Design Pattern_)。
假如咱们须要给一家火锅店设计一套结账零碎,也就是统计顾客生产的总价格。怎样才能设计出一个好的零碎呢?
1,结账零碎需要剖析
既然要设计一个结账零碎,当然须要晓得火锅店都有哪些食品及食品的价格,如果咱们从火锅店老板那里失去以下食品清单:
锅底类:
- 清汤锅底:5 元
- 麻辣锅底:7 元
- 其它
配菜类:
- 青菜:3 元
- 羊肉:6 元
- 其它
饮料类:
- 可乐:2 元
- 其它
能够看到,食品共有三大类,别离是:锅底类,配菜类和饮料类,每个大类下边都有很多具体的食品。
为了设计出一个可保护,可扩大,有弹性的零碎,应该怎么设计呢?
咱们能够这样对待食品之间的关系,将锅底类看作主品,所有其它的都为副品,也就是附加在主品之上的食品。
副品以主品为核心,围绕在主品四周,包裹着主品,一层层的往外扩大。
如下图所表白的一样:
2,装璜者模式
像这种,须要在原来(主品)的根底上,附加其它的货色(副品),这样的业务场景都能够应用装璜者模式。
装璜者模式的定义为:动静的给一个对象增加其它性能。从扩展性来说,这种形式比继承更有弹性,更加灵便,可作为代替继承的计划。
装璜者模式的长处在于,它可能更灵便的,动静的给对象增加其它性能,而不须要批改任何现有的底层代码。也就是不须要通过批改代码,而是通过扩大代码,来实现新的业务需要。
这就十分合乎咱们所说的设计准则中的开闭准则:对扩大凋谢,对批改敞开。也就是尽量不要批改原有代码,而是通过扩大代码来实现工作。这样做的益处是能够缩小对原有零碎的批改,从而缩小引入 bug 的危险。
装璜者模式的类图如下:
ConcreteComponent 为被装璜者,Decorator 是所有装璜者的超类。
装璜者和被装璜者有着独特的超类型,这一点很重要,因为装璜者必须可能取代被装璜者。这样,装璜者能力在被装璜者的根底上,加上本人的行为,以加强被装璜者的能力。
一个被装璜者能够被多个装璜者顺次包装,这个包装行为是动静的,不限次数的。
3,实现结账零碎
那么依据装璜者模式的类图,咱们能够设计出火锅店结账零碎的类图,如下:
火锅的锅底作为被装璜者,配菜和饮料作为装璜者。
每个类都有两个办法:
- describe:返回以后火锅的形容。
- cost:返回以后火锅的价格。
首先编写 HotPot
类:
class HotPot { protected String desc = "HotPot"; protected double price = 0; public String description() { return desc; } public double cost() { return price; } public void printMenu() { System.out.println("菜单:" + description() + " 生产总价:" + cost()); }}
HotPot
类中有两个属性 desc
和 price
,还有三个办法 description
,cost
和 printMenu
。printMenu
用于输入菜单和生产总价。
再编写 SideDish
类:
class SideDish extends HotPot { protected HotPot hotpot; public double cost() { return hotpot.cost() + price; }; public String description() { return hotpot.description() +" + "+ desc; };}
SideDish
继承了 HotPot
,增加了本人的属性 hotpot
,并且重写了两个办法 description
和 cost
。
留神:SideDish
类对两个办法 description
和 cost
进行了重写,这十分重要,这体现出了装璜者与被装璜者的区别,装璜者能在被装璜者的根底上附加本人的行为,起因就在这里。
编写两个锅底类:
class SoupPot extends HotPot { public SoupPot() { desc = "Soup"; price = 5; }}class SpicyPot extends HotPot { public SpicyPot() { desc = "Spicy"; price = 7; }}
这两个类都继承HotPot
,并别离在构造方法中设置本人的 desc
和 price
。
再编写三个配菜类:
class VegetablesDish extends SideDish { public VegetablesDish(HotPot hotpot) { this.hotpot = hotpot; desc = "Vegetables"; price = 3; }}class MuttonDish extends SideDish { public MuttonDish(HotPot hotpot) { this.hotpot = hotpot; desc = "Mutton"; price = 6; }}class ColaDish extends SideDish { public ColaDish(HotPot hotpot) { this.hotpot = hotpot; desc = "Cola"; price = 2; }}
这三个类都继承 SideDish
,并别离在构造方法中设置本人的hotpot
,desc
和 price
。
4,测试结账零碎
用如下代码来测试:
// 只有一份清汤锅底HotPot hotpot = new SoupPot(); // 被装璜者不需装璜者包装也能够应用hotpot.printMenu();// 清汤锅底 + 蔬菜hotpot = new VegetablesDish(hotpot);hotpot.printMenu();// 清汤锅底 + 蔬菜 + 羊肉hotpot = new MuttonDish(hotpot);hotpot.printMenu();// 清汤锅底 + 蔬菜 + 羊肉 + 可乐hotpot = new ColaDish(hotpot);hotpot.printMenu();// 清汤锅底 + 蔬菜 + 羊肉 + 可乐 + 蔬菜hotpot = new VegetablesDish(hotpot);hotpot.printMenu();
输入如下:
菜单:Soup 生产总价:5.0菜单:Soup + Vegetables 生产总价:8.0菜单:Soup + Vegetables + Mutton 生产总价:14.0菜单:Soup + Vegetables + Mutton + Cola 生产总价:16.0菜单:Soup + Vegetables + Mutton + Cola + Vegetables 生产总价:19.0
计算总价时,会从最外层的装璜者,朝着被装璜者的方向,顺次调用每一层的 cost
办法,直到被装璜者为止。
而后再朝着最外层装璜者的方向,顺次计算出每一层的价格,最初得出的价格就是生产总价。
计算过程如下图所示:
我将残缺的装璜者模式代码放在了这里,供大家参考。
5,装璜者模式的应用场景
装璜者模式次要用于,在不批改原有类的前提下,动静的批改原有类的性能。
Java JDK 中大量应用了装璜者模式,尤其是 Java I/O 框架。
Java IO 框架的继承关系如下: