当你去买手抓饼时,可以选择最基础的,也可以选择加蛋的,加肠的,或者加肠加蛋的。假如我们在编程中遇到了类似的问题:已经有了一个最基础的手抓饼类(它继承了手抓饼接口),如何修改代码使得可以创建加肠版本的手抓饼或者加蛋版本的手抓饼呢?
也许最简单的直觉是再创建两个类继承手抓饼接口,但是这样还要在这两个版本中把基础手抓饼制作代码再重复一遍,然后进行加肠或加蛋;但其实我们只需要额外添加肠或者添加蛋的代码就够了。而且,如果我要同时加肠加蛋的,难道还要再创建一个类吗?假如有更多可加的东西,它们组合一下得创建多少个类啊!
这时,装饰器模式出现了,它可以帮你解决上述问题。
(图片来自:https://www.cnblogs.com/octobershiner/archive/2011/11/04/2236730.html)
对于刚才的问题,手抓饼接口就是图中的原始类,基础版本手抓饼是继承类A。应用装饰器模式,我们要创建一个高级手抓饼的基类,加肠、加蛋就是装饰器A、B。那么到底是如何通过这个模式防止基础代码的重复并可使不同的额外部分进行组合(同时加肠与加蛋)的呢?有以下代码示例。
package DesignPattern;
public class Decorator {
public static void main(String[] args) {
Pizza pizza = new BasicPizza();
System.out.println("------Basic pizza------");
pizza.make();
pizza = new MeatPizza(new BasicPizza());
System.out.println("------Meat pizza------");
pizza.make();
//下面pizza的创建方式展示了如何组合不同的装饰器功能
pizza = new FruitPizza(new MeatPizza(new BasicPizza()));
System.out.println("------Meat and Fruit pizza------");
pizza.make();
}
}
//被装饰对象的接口
interface Pizza {
void make();
}
//被装饰对象,目前还未被装饰
class BasicPizza implements Pizza{
public void make() {
System.out.println("add flour!");
}
}
//装饰器的基类
abstract class AdvancedPizza implements Pizza{
protected Pizza basic;
AdvancedPizza(Pizza _basic) {
basic = _basic;
}
}
//装饰器
class MeatPizza extends AdvancedPizza {
MeatPizza(Pizza _basic) {
super(_basic);
}
public void make() {
basic.make(); //调用基础版本的方法,防止代码重复
addMeat();
}
void addMeat() {
System.out.println("add meat!");
}
}
//装饰器
class FruitPizza extends AdvancedPizza {
FruitPizza(Pizza _basic) {
super(_basic);
}
public void make() {
basic.make();
addFruit();
}
void addFruit() {
System.out.println("add Fruit!");
}
}
/*output:
------Basic pizza------
add flour!
------Meat pizza------
add flour!
add meat!
------Meat and Fruit pizza------
add flour!
add meat!
add Fruit!
*/
也许有这种疑问:为什么装饰器基类要去继承原始类?就算不继承直接使用组合的方式在装饰器中引入原始类对象不也可以防止代码重复吗?
但是这样有两个缺点:
1.装饰器无法与原始类的对象进行替换(即加肠的手抓饼不是手抓饼),这样会在其它部分的代码使用中造成麻烦;让装饰器基类继承原始类接口,使用装饰器还是使用原始对象对用户来说是透明的,不用额外考虑我是在用原始类还是装饰器。
2.我们进行不同装饰器功能的组合时(同时加肠和蛋),其实是通过将一个装饰器传入另一个装饰器的构造函数中(这里本该是原始类),从而在调用原始类的功能时实际上调用了另一个装饰器的功能,完成了不同装饰器功能的复合。如果装饰器基类不继承原始类接口,是难以完成这种功能的。