设计模式学习笔记10-Responsibility(职责链)模式

本文主要是看了《设计模式》做的笔记和思考,在此分享仅代表个人观点,如有不对的地方欢迎批评和指正。

基础

Responsibility模式,中文名职责链模式,是一种可以将请求沿着类之间的关系链一直传递下去,直到有适合的类处理或者忽视它。适用于需求方明确,但供给方不确定的情况。其UML如下所示。


Responsibility-UML图.png

解释一下:这里的“继承者”表示职责链中的下一个节点。

在使用该模式时,需要先创建出处理一般请求的对象(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. 表示请求

有如下几种办法:

  1. 固定函数
  2. 传递参数,使用类似枚举、字符串等形式进行区分
  3. 传递请求类,请求类还可以通过继承实现多样化

最直接的办法是用固定的函数写死,这样每种函数都成为一条职责链。若要共用一条职责链,那么传递参数时可以考虑使用枚举、字符串等形式,到时使用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类完成多样的请求封装。另外如果你有其他结构型设计模式,那么可以直接利用已有的关系网实现职责链。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,641评论 6 525
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,754评论 3 408
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 173,075评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,369评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,386评论 6 402
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,800评论 1 317
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,122评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,130评论 0 281
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,689评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,693评论 3 348
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,804评论 1 356
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,399评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,128评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,528评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,705评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,409评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,878评论 2 368

推荐阅读更多精彩内容