本文主要是看了《设计模式》做的笔记和思考,在此分享仅代表个人观点,如有不对的地方欢迎批评和指正。
基础
Responsibility模式,中文名职责链模式,是一种可以将请求沿着类之间的关系链一直传递下去,直到有适合的类处理或者忽视它。适用于需求方明确,但供给方不确定的情况。其UML如下所示。

解释一下:这里的“继承者”表示职责链中的下一个节点。
在使用该模式时,需要先创建出处理一般请求的对象(General类),然后是特殊请求的对象(Special类),并将General类的引用作为Special类的一个属性,使得Special类在不能处理当前请求时可以抛给General类进行更一般化的处理。
这个过程可以用一个问路的形式表现:
房间的人(不知道诶,问下外面的保安)-->管层的保安(好像不在这一层,你问下头吧)-->管楼的保安(你搞错了,对面的)-->管小区的物业(你这么走这么走……)
示例代码
这里再说另一个例子:送快递。
省到省之间大家至少知道说的是哪两个省,但市与市之间可能连名字也没听过,所以我们可以这么抽象:省之间的运输属于一般性处理,省会城市到其他市(或者反过来)是特殊处理。
那么随便来两个地址:
from: 浙江省杭州市滨江区网商路699号
to: 广东省深圳市南山区高新科技园北区
首先,网商路的小哥拿着快递看到是“广东省”,默默地把它装上了运往滨江区的车;到了滨江区,小哥看到“广东省”立刻扔给了去杭州市的车;到了杭州市,杭州市的运转中心就把它送去广东省了。那么,写成代码就是这样的:
class RoadCr extends Courier{
private TownCr town;
// 假如是本街区的他也会送
public void handle(string to){
if(属于本街区的){
// send it
}else{
town.handle(to);
}
}
}
class TownCr extends Courier{
private ProvinceCr province;
public void handle(string to){
if(属于本镇的){
// send it
}else{
province.handle(to);
}
}
}
class ProvinceCr extends Courier{
public void handle(string to){
if(属于本市的){
// send it
}else{
// send other province
}
}
}
void Main(){
string to = "广东省深圳市南山区高新科技园北区";
new RoadCr(new TownCr(new ProvinceCr)).handle(to);
}
这个例子中,快递小哥能够处理时就自己处理,不能处理时就抛给上一层的人处理,这个快递就是沿着一条职责链从一端到达另一端。那么到了广东省怎么处理?这里就需要用到Composite(组合)模式了,我们可以把多个市的组合体看成是一个省,那么这个省(多个市的组合体)就会有所有市的引用,我们这个例子的后续部分将通过调用组合体内部成员的方法来完成。
注意事项
1. 创建和连接后继者
在上面的例子中,后继者与该Handler本身是创建后手动关联起来,如果使用的是类似Composite这种模式,类与类之间本身已经有了联系,可以借助这个联系直接使用职责链模式。注意,尽管每个类中包含了对其他类的引用时便可以实现职责链模式,你需要小心请求在这条链上的流向,毕竟这个模式中请求是不知道自己将被哪个Handler处理。
2. 表示请求
有如下几种办法:
- 固定函数
- 传递参数,使用类似枚举、字符串等形式进行区分
- 传递请求类,请求类还可以通过继承实现多样化
最直接的办法是用固定的函数写死,这样每种函数都成为一条职责链。若要共用一条职责链,那么传递参数时可以考虑使用枚举、字符串等形式,到时使用switch或者是if加以判断就好。
那么请求类呢?就拿右键屏幕控件的例子来说,如果还需要右键的位置呢?比如微软桌面上右键,正常来说是以鼠标为中心往右下方展开右键菜单,那么为了防止右下方空间不够,可能需要拿到右键时的位置加以判断和处理。
class ClickRequest{
public float x;
public float y;
public int flag;
}
class ApplicationHandler extends Handler{
public void handle(ClickRequest r){
if(r.flag == Request.APPLICATION){
execute(r);
}
}
public void execute(ClickRequest r){
if(width - r.x < 0 || height - r.y < 0){
...
}
}
}
使用第3种方法虽然略显麻烦,但拥有很大的灵活性,如果你接收的不是ClickRequest而是它的父类Request(假如我们上面继承了Request),那么可以直接通过判断类的名字来达到类似枚举的效果,在Java中可以使用instanceof关键字进行,强制转换后,就能直接拿出Request中的额外信息。
public void handle(Request r){
if(r instanceof ClickRequest){
...
}else if(r instanceof DragRequest){
...
}
}
总结
这个模式主要精髓是职责链可以将请求一路传递,而且新的Handler是添加旧Handler的引用作为后继者,能够灵活应对新需求并且不需要修改什么代码。如果请求也有一定的不确定性,可以让Handler处理Request类,然后通过继承Request类完成多样的请求封装。另外如果你有其他结构型设计模式,那么可以直接利用已有的关系网实现职责链。
网友评论