明天来介绍 装璜者模式(_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 框架的继承关系如下: