装饰模式是第三个介绍的模式了.
这个模式没有前面两个那么好理解.,
一, 装饰模式(decorator)的定义.
教材里是这样写的:
动态第给1个对象添加1写额外的职责(功能), 就增加的功能来讲, 装饰模式比生成子类更加灵活.
就咁睇的确很难明白. 但我们起码可以知道一下两点:
1. 装饰模式的作用是增强1个对象(类) 的功能.
2. 它比生成子类更加灵活.
本文着重讲下第2点, 包括什么是生成子类, 为什么装饰模式比它更加灵活.
二, 1个Hunter1(猎人)类的例子.
举1个很简单的例子. 1个猎人类实现1个打猎的接口.
Huntable 接口:
public interface Huntable {
public void hunt();
}
猎人类Hunter1:
public class Hunter1 implements Huntable{
public void eat(){
System.out.println("eating!");
}
public void hunt(){
System.out.println("Hunter is Hunting!");
}
}
可见Hunter1这个类实现了Huntable借口.
hunt()方法里只简单重写了hunt()方法.
客户端代码:
Hunter1 h = new Hunter1();
h.hunt();
输出:
Hunter is Hunting!
1个增强hunt()方法的需求:
好了, 现在有1个猎人, 他出去不只是打猎, 他打猎的同时还同时训练它的猎狗, 最后还会给战利品拍一张照片.也就是需要增强hunt()方法功能.
加上
trainDog() 和 takePic()这个两个方法.
怎么实现呢?
三, 通过添加子类Hunter2方法来增强Hunter1
open-closed principle:
可能有人会觉得. 直接在Hunter1里加上两个方法trainDog () 和 takePic(), 在hunt()里调用这个两个新方法不就完事了吗.
的确, 但是这种做法修改了原有的类, 违反了"封闭-开放原则".
所谓"封闭-开发原则" 就是对修改封闭, 对扩展开放.
在项目中, 很多旧的业务类根本不会让你修改(jar包发布或者你没有权限)
所以我们应该把这个新需求当做1个扩展来处理, 而不是修改旧代码.
其中1个方法就是增加子类.
新增1个Hunter2 类:
public class Hunter2 extends Hunter1{
@Override
public void hunt(){
super.hunt();
this.trainDog();
this.takePic();
}
//new function()
public void trainDog(){
System.out.println("Train the Dog!");
}
public void takePic(){
System.out.println("Take a picture!");
}
}
客户端代码:
Hunter2 h2 = new Hunter2();
h2.hunt();
UML:
可见, 这种方法, 只是新增了1个子类, 以及修改客户端代码, 对原来的类代码没有任何修改, 是符合"封闭-修改 原则"的.
这种修改, 增强了原来的类Hunter1 的hunt()功能, 可以说是"装饰"了原来的hunt()方法.
但是这种修改不是很灵活.
比如: 这种猎人是先打猎,再训狗,再拍照的.
如果有一种猎人喜欢先拍照再训狗了, 我们就又要增加1个Hunter1的子类了....
而装饰模式能解决问题.
四, 通过装饰模式来增强Hunter类.
Huntable 接口 和 Hunter类:
首先构造两个相同的接口和类:public interface Huntable {
public void hunt();
}
public class Hunter implements Huntable {
public void eat(){
System.out.println("eating!");
}
@Override
public void hunt(){
System.out.println("Hunter is Hunting!");
}
}
Hunter类跟上面的Hunter1类是完全一样的.
下面开始构造新的类来强化hunt()方法.
抽象装饰角色类 HuntOperation:
无论, 是训狗trainDog() 还是拍照takePic() 都是对hunt()的增强.
我们新增1个HuntOperation类, 让它继承Hunter类, 让它能重写hunt()方法.
public abstract class HuntOperation extends Hunter{
private Hunter hunter = null;
//setting component
public HuntOperation(Hunter hunter){
this.hunter = hunter;
}
//actually execute component's hunt() method
@Override
public void hunt(){
if (null != hunter){
hunter.hunt();
}
}
}
注意, 这个类有1个Hunter对象成员.
在hunt()方法里, 执行的实际上是成员Hunter对象的hunt()方法.
具体的装饰行为类 HuntTakePic():
照相类, 就让它继承抽象行为类 HuntOperation类.
public class HuntTakePic extends HuntOperation{
public HuntTakePic(Hunter hunter) {
super(hunter);
}
@Override
public void hunt(){
super.hunt();
this.takePic();
}
public void takePic(){
System.out.println("Taking a Picture!");
}
}
可见, 它先执行性成员Hunter对象的hunt()方法, 在执行自己的照相方法.
具体的装饰行为类 HuntTrainDog():
同样, 这个类也继承HuntOperation.先执行Hunter对象的hunt()方法, 再执行自己的TrainDog方法.
public class HuntTrainDog extends HuntOperation{
public HuntTrainDog(Hunter hunter) {
super(hunter);
// TODO Auto-generated constructor stub
}
@Override
public void hunt(){
super.hunt();
this.trainDog();
}
public void trainDog(){
System.out.println("Training the dog!");
}
}
客户端代码:
客户端代码如下:先构建1个Hunter对象h, 然后再基于h构建HuntTakePic htp, 然后在基于htp构建HuntTrainDog 对象htd.
最后执行htd的hunt()方法.
Hunter h = new Hunter();
HuntTakePic htp = new HuntTakePic(h);
HuntTrainDog htd = new HuntTrainDog(htp);
htd.hunt();
实际上, 就先执行Hunter对象的hunt(), 再执行HuntTakePic对象的hunt(), 最后才执行HuntTrainDog对象的hunt()
输出:
Hunter is Hunting!
Taking a Picture!
Training the dog!
UML:
装饰模式的灵活性:
好了, 这种模式一样能增强Hunter类的hunt()
那么它到底比第一种方法灵活在哪里呢.
很简单, 假如另1类猎人想调换训狗, 和照相的顺序, 只需修改客户端的构建循序, 并需要再新增1个子类.
这就是装饰模式的灵活性.
有人会觉得, 装饰模式同样需要增加子类啊, 实际上, 假如我们为Hunter的hunt()再增加1个子行为, 例如给猎物剥皮.
那么只需要增加1个抽象行为HuntOperation的子类, 而不是Hunter的子类..
五, 小结
其实想深一层, 装饰模式之所以叫装饰模式.
是因为装饰不能改变原有的功能的行为.
就如上面的例子, Hunter里原来的hunt()方法没有改变, 只是增加了一些额外的功能.
所以如果需要从根本上改变Hunter里hunt()的代码, 装饰模式就不适用了, 还是建议用增加Hunter子类的方法.