初看责任链模式,心里不禁想起了一个以前听过的相声:看牙。说的是一个病人看牙的时候,音声不小心把拔下的牙掉进病人的嗓子里。病人因此楼上楼下的跑了好多可是,最后无果而终。
责任链模式就是这种“推卸”责任的模式,你的问题在我这里能解决我就解决,不行就把你推给另一个对象。至于到底谁解决这个问题,我不管。
Chain of Responsibility
定义
Chain of Responsibility(CoR)是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一一个共同点式在他们之间传递request。也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理就传递到C类处理。就这样象一个链条(Chain)一样传递下去。
避免请求发送者与接受者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条连,并且沿着这条链传递请求,直到有对象处理它为止。
二、类与对象的关系
传递着接口(Handler
):定义一个处理请求的接口,(可选)实现链中下一个对象。
具体传递着(Concrete Handler
):处理它所负责的请求,可以访问链中的下一个对象,如果可以处理请求,就处理它,否则将请求转发给后继者。
客户应用程序(Client
):向链中的对象提出最初的请求。
三、范例:1.军队中剧情的传递就是一个责任链,下属行动需要上级比准,所以要把行动计划包给上级,上级有传给上级,直至有一个人可以决定,一个士兵请求进入反恐行动组,他就会给班长发送请求,班长给排长,排长个连长,这样一级一级的传下去,直到传到军长,因为军长能够据顶谁可以参加反恐行动组。
2.既定我们要做一个只能大厦按群系统。安全系统包括很多各种传感器(运动检测器、烟感检测器、温度检测器等),它们的状态会传给计算机。计算机系统的工作是记录这些状态并在紧急事件发生时发出警报。我们设计的系统要有高度可扩展性,它可以适用于小型零售店、办公楼、仓库、多功能大厦等多种场合。
四、优势和缺陷
责任链模式可以减少对象的连接,为对象责任分配增加了很大的灵活性。该模式允许把一组类作为一个类来使用,并且在类的组合中,一个累的时间可以发送到另一个类并有其处理。
责任链模式通常应用于图形用户界面中,窗体的部件可能会包含其他几个小部件,就如同windows窗体应用程序中,控件中又可以防止其他控件,控件编辑诶会决定是否处理事件,或者将时间传递给父控件来处理。
另外,责任链还会以树状出现,这样,一个时间可以传给多个类,或者,多个类的信息可以提交到一个类。树状责任链能够提供更灵活的技巧,但缺点是信息在树中容易迷失。
五、应用情景
超过一个对象能够处理客户请求并且到底哪个对象处理预先不知道。
一个请求可以发布到多个对象但它的接受都是不清晰。
可以动态之地昂一组对象处理请求。
六、如何使用
虽然这一段是如何使用CoR,但是也是演示什么是CoR。
有一个Handler接口:
public
interface
Handler {
public
void
handleRequest();
}
|
这是一个处理request的事例,如果有多种request,比如:请求帮助、请求打印、请求格式
化
最先想到的解决方案就是:在接口中增加多个请求。
public
interface
Handler {
public
void
handleHelp();
public
void
handldPrint();
public
void
handleFormat();
}
|
在具体写一段实现Handler代码:
public
class
ConcreteHandler
implements
Handler {
private
Handler
handler
;
public
ConcreteHandler(Handler handler) {
this
.
handler
= handler;
}
public
void
handleHelp() {
//
具体怎么样去完成帮助
}
public
void
handlePrint() {
//
具体怎么样去完成打印
}
public
void
handleFormat() {
//
具体怎么样去完成格式化
}
}
|
一共有三个这样的具体实现类,上面是处理help,还有处理Print,处理Format这大概是我们最常用的编程思路。
虽然思路简单明了,但是有一个扩展问题,如果我们需要在增加一个请求request种类,需要修改接口及其每一个实现。
第二种方案:将每种request都变成一个接口,因此我们有以下代码:
public
interface
HandleHelp {
public
void
handleHelp();
}
public
interface
HandlePrint {
public
void
handlePrint();
}
public
interface
HandleFormat {
public
void
handleFormat();
}
|
public
class
ConcreteHandler
implements
HandleHelp, HandlePrint, HandleFormat {
private
HandleHelp
handleHelp
;
private
HandlePrint
handlePrint
;
private
HandleFormat
handleFormat
;
public
ConcreteHandler(HandleHelp handleHelp, HandlePrint handlePrint,
HandleFormat handleFormat) {
this
.
handleHelp
= handleHelp;
this
.
handlePrint
= handlePrint;
this
.
handleFormat
= handleFormat;
}
public
void
handleHelp() {
//
具体怎么样去完成帮助
}
public
void
handlePrint() {
//
具体怎么样去完成打印
}
public
void
handleFormat() {
//
具体怎么样去完成格式化
}
}
|
这个方法在增加新的请求request情况下,只是节省了接口的修改量,
接口实现ConcreteHandle还需要修改。而且代码显然不简单美丽。
解决方案三:在Handler接口中只使用一个参数化方法:
public
interface
Handler {
public
void
handleRequest(String str);
}
|
那么Handler实现代码如下:
public
class
ConcreteHandler
implements
Handler {
private
Handler handler;
public
ConcreteHandler(Handler handler) {
this
.handler = handler;
}
public
void
handleRequest(String str) {
if
(str.equals(
"help"
)) {
//
具体完成帮助
}
else
{
//
传递到下一个类去实现
}
}
}
|
这个方案是先假设request是String类型,如果不是怎么办?当然我们可以创建一个专门类Request
最后的解决方案:接口Handler的代码如下:
public
interface
Handler {
public
void
handleRequest(Request request);
}
|
Request类的定义如下:
public
class
Request {
private
String
str
;
public
Request(String str) {
this
.
str
= str;
}
public
String getStr() {
return
str
;
}
public
void
executed() {
//
具体的请求
}
}
|
ConcreteHandler类的定义如下:
public
class
ConcreteHandler
implements
Handler {
private
Handler
handler
;
public
ConcreteHandler(Handler handler) {
this
.
handler
= handler;
}
public
void
handleRequest(Request request) {
if
(request
instanceof
HelpRequest
) {
//
具体帮助
}
else
{
request.executed();
//
交给其他的类去实现
}
}
}
|
这个解决方案就是CoR,在一个链上,都已相应职责的类,因此叫Chain of Responsibility。
七、CoR
的优点
因为无法预知来自外界(客户端)的请求时数以那种类型,每个类如果碰到它不能处理的请求只要放弃就可以。
缺点是效率低,因为一个请求的完成可能要遍历到最后才可能完成,当然也可以用树的概念优化。在java AWT1.1中,对于鼠标按键事件的处理就是使用CoR,到java 1.1后,就是用Observer代替CoR,它的扩展性差,因为在CoR中,一定要有一个统一的接口Handler,局限性就在这里。
八、与Command
模式的区别
Command 模式需要实现协商客户端和服务器端的调用关系,比如1代表Start2代表move等,这些都是封装在Request中,到达服务器端在分解。
CoR模式就无需这种事先约定,服务器端可以使用CoR模式进行客户端请求的猜测,一个一个猜测试验。