装饰模式(Decorator)简介

来源:互联网 发布:java运算符有哪些 编辑:程序博客网 时间:2024/06/05 12:06

装饰模式是第三个介绍的模式了.

这个模式没有前面两个那么好理解.,


一, 装饰模式(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{@Overridepublic 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!");}@Overridepublic 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 componentpublic HuntOperation(Hunter hunter){this.hunter = hunter;}//actually execute component's hunt() method@Overridepublic 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);}@Overridepublic 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}@Overridepublic 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子类的方法.





















0 0