设计模式第四篇,装饰者模式,大家多多指教。

简介

装饰者模式是动态的将责任附加到对象上(引自《Head First设计模式》)。这里的重点在于动态这两个字,我们都知道继承的实现的方式,它是是类编译的时候就去加载文件,属于一种静态的附加,而我们要实现动态的附加就不能单纯的通过继承来实现。在这种背景下,装饰者模式就应运而生了。装饰者模式的实现:首先所有的类都有一个共同的抽象,这个抽象可以是一个抽象类,也可以是一个接口,所有的类该抽象的子类或者实现。语言描述比较抽象,下面我们通过一个例子来描述该模式。

例子

我们举一个吃火锅的例子,根据装饰者模式的特点:

首先我们要有个共同的抽象,这里我们通过接口来实现,这个接口我们称之为锅底。

/** 锅底*/
public interfaceHotpot {/**描述*/String getName();/**价格*/Double getPrice();
}
第二步,我们设计具体的锅底类,我们分为酱香锅、香辣锅和清汤锅,这是底锅,每笔单都必须有且只有一个。
public class JamHotpot implementsHotpot{
@OverridepublicString getName() {return "酱香锅";
}
@OverridepublicDouble getPrice() {return 4.0;
}
}public class SpicyHotpot implementsHotpot{
@OverridepublicString getName() {return "香辣锅";
}
@OverridepublicDouble getPrice() {return 4.0;
}
}public class ClearSoupHotpot implementsHotpot{
@OverridepublicString getName() {return "清汤锅";
}
@OverridepublicDouble getPrice() {return 3.0;
}
}

第三步,我们新增一个配菜接口,继承共同的接口锅底,这个接口用来作为所有配菜的父级,该接口可以拓展所以配菜的共有属性

/** 配菜*/
public interface SideDish extendsHotpot {//可拓展配菜的公共属性
}

第四步,我们需要新增具体的配菜类,这里的配菜类我们注意到比锅底类多了成员变量和构造方法,而且getName和getPrice方法多了加了传入参数,这个就是为了动态的去附加功能,具体怎么实现参考最后一步。

/** 牛肉*/
public class Beef implementsSideDish {privateHotpot hotpot;publicBeef(Hotpot hotpot) {this.hotpot =hotpot;
}
@OverridepublicString getName() {return hotpot.getName() + ",牛肉";
}
@OverridepublicDouble getPrice() {return hotpot.getPrice() + 38.0;
}
}/** 生菜*/
public class Lettuce implementsSideDish {privateHotpot hotpot;publicLettuce(Hotpot hotpot) {this.hotpot =hotpot;
}
@OverridepublicString getName() {return hotpot.getName() + ",生菜";
}
@OverridepublicDouble getPrice() {return hotpot.getPrice() + 8.0;
}
}

最后我们测试:

public classDecoratorTest {public static voidmain(String[] args) {
System.out.println("=======酱香锅底加牛肉");
Hotpot hotpot= newJamHotpot();
hotpot= newBeef(hotpot);
hotpot= newLettuce(hotpot);
System.out.println("===火锅的价格为:" + hotpot.getPrice() + "元");//如果我们需要更多的配菜参考上面的方式去添加更多的配菜
}
}

总结

如上面所描写的那样,我只需要前台传递给我们所点的锅底和配菜,我们后台去动态的生成一个新的火锅对象。这么设计的好处的拓展极其方便,就算这个火锅店加了新的锅底,或者是配菜,对我们之前的代码是没有任何影响的,这就是所谓的开闭原则(对拓展开放,对修改关闭),后面我们会讲到设计模式相应的原则。除了上面的好处之外,装饰者模式也有他本身不能忽视的问题,那就是每次生成一笔订单都会生成很多小对象,如果该笔单对象很多的话,对我们的内存是个不小的考验,而且过多的小类会让我们的系统变得很复杂,让人很难以理解。所以装饰器模式有利有弊(更多的是利大于弊),我们可以好好思考装饰者模式应用场景,然后进一步掌握这种设计思想。

番外

想了解装饰者模式的更多详细信息可以通过jdk源码或者spring源码去了解。jdk中IO操作就有通过装饰者模式实现的,FilterInputStream和它的实现就是一个装饰者模式,同样的在spring源码中,凡事以Decorator结尾的类都是实现的装饰者模式,当然不是所有的装饰器模式都是以Decorator结尾的。