职责链模式(chain of responsibility)

原文地址

一. 写在前面的

这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式。

因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理。所以希望各位多多指教。

二. 什么是链


文章伊始,先让我们了解这个最基本的概念,什么是链。

我给链下了这样的定义:

  1. 链是一系列节点的集合。
  2. 链的各节点可灵活拆分再重组。

三. 何为职责链

职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。

图如下:

四. 职责链模式应用之请假管理

请假这个事情,相信每个人都不陌生。

我们公司是个相对很宽松的公司。

在公司里,如果你的请假时间小于0.5天,那么只需要向项目经理打声招呼就OK了。

如果超过了0.5天,但是还小于2天,那么就要去找人事部处理,当然,这就要扣工资了。

如果超过了2天,你就需要去找总经理了,工资当然也玩完了。

那么,对于我们来说,这个流程就是这样的。

也就是这样一个过程,你需要和你的直接上级——项目经理去打交道,最终可能是项目经理给你回邮件,可能是人事部给你回邮件,也可能是总经理给你回邮件。内部的过程其实应该是个黑盒子,你并不知道内部的消息是如何处理的。你需要找到的,只是你想要第一个交付的对象而已。

那么我们的代码应该是这样的。

首先我们要写一个请求的类。

class Request
{
    private int day;
    private string reason;
    public int Day
    {
        get { return day; }
        set { day = value; }
    }
    public string Reason
    {
        get { return reason; }
        set { reason = value; }
    }
    public Request(int day, string reason)
    {
        this.day = day;
        this.reason = reason;
    }
}

接下来看下请求相应者,他们有两个核心方法,一个是相应操作,一个是选择继任者。

abstract class Boss
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    private Boss successor;
    public Boss Successor
    {
        get { return successor; }
        set { successor = value; }
    }
    public Boss(string name)
    {
        this.name = name;
    }
    public abstract bool PassRequest(Request request);
}
class PM:Boss
{
    public PM(string name)
        : base(name)
    { }
    public override bool PassRequest(Request request)
    {
        int day = request.Day;
        string reason = request.Reason;
        if (day <= 0.5)
        {
            return true;
        }
        return Successor.PassRequest(request);
    }
}
class HR:Boss
{
    public HR(string name)
        : base(name)
    { }
    public override bool PassRequest(Request request)
    {
        int day = request.Day;
        string reason = request.Reason;
        if (day > 0.5&&day<=2)
        {
            return true;
        }
        return Successor.PassRequest(request);
    }
}
class Manager : Boss
{
    public Manager(string name)
        : base(name)
    { }
    public override bool PassRequest(Request request)
    {
        int day = request.Day;
        string reason = request.Reason;
        if (reason.Equals("正当理由"))
        {
            return true;
        }
        return false;
    }
}

那么我们调用的时候就很简单了!

static void Main(string[] args)
{
    Request request = new Request(3, "非正当理由");
    Boss pm = new PM("pm");
    Boss hr = new HR("hr");
    Boss manager = new Manager("manager");
    pm.Successor = hr;
    hr.Successor = manager;
    bool pass = pm.PassRequest(request);
    Console.Write(pass);
}

五. 灵活在哪?

让我们来看下职责链究竟灵活在哪?

  1. 改变内部的传递规则。

    在内部,项目经理完全可以跳过人事部到那一关直接找到总经理。

每个人都可以去动态地指定他的继任者。

2.可以从职责链任何一关开始。

如果项目经理不在,那么完全可以写这样的代码:

static void Main(string[] args)
{
    Request request = new Request(3, "非正当理由");
    Boss pm = new PM("pm");
    Boss hr = new HR("hr");
    Boss manager = new Manager("manager");
    pm.Successor = hr;
    hr.Successor = manager;
    //bool pass = pm.PassRequest(request);
    bool pass = hr.PassRequest(request);
    Console.Write(pass);
}

3、我们来比较一下,用职责链和不用职责链的区别:

这是不用职责链我们的结构,我们需要和公司中的每一个层级都发生耦合关系。

如果反映在代码上即使我们需要在一个类中去写上很多丑陋的if….else语句。

如果用了职责链,相当于我们面对的是一个黑箱,我们只需要认识其中的一个部门,然后让黑箱内部去负责传递就好了。

六. 职责链 != 链表

很多人都愿意把职责链和链表混为一谈,确实,从字面意思上理解,链,链表,很像。可是他们一样么?

他们区别在哪里:

让我们看一个链表的典型结构:

让我们来看一下链表的典型特征:

  1. 链表是一个链状结构,每个节点有一个next属性去指向他的下一节点。
  2. 链表有一个Header节点,然后用户每次必须通过头节点,然后去遍历寻找每一个节点。
  3. 链表遍历操作的复杂度是O(n),但是插入和删除指定节点的复杂度是常数级。

让我们来着重看这第二点:

我们来想想在文章开始时我们画出的那个链,一个链,我们可以从头将他拿起,也可以从中间将他拿起:

也就是说我们用户可以去访问节点中的任何一个节点作为开始节点,这就是链表与职责链不同的地方。

七. 职责链的扩展——树状链结构

职责链中,我们之前看到的都是一些单链结构,但是其实在很多情况下,每一个节点都对应着很多其他的部分。

那么这样,我们的每一个节点都可以使用一个List来维护他节点的下一节点,甚至可以用组合模式来分别设计每一节点。

八. 由法律想到——职责链的兜底条款

仔细想想法律条文,尤其是刑法,经常可以看到这样的条文:

  1. 如果***,则处以拘役处分。
  2. 如果***,则处以有期徒刑一年到十年。
  3. 如果***,则处以有期徒刑十年以上。
  4. 如果*,则**
  5. 如果以上条件皆不满足,则*******

其实最后一条就叫做法律的兜底条款。这给了法官很大的自由裁量权,在一定程度上也降低了犯罪分子钻法律空子的可能性。

在我们的职责链中,如果不存在这样的兜底条款,那么用户如果不从首节点开始访问,那么就很可能出现异常的情况。于是我们应该为职责链设置一个默认的条款:

这样的话,任何一个处理无论如何访问,都能得到一个正常的处理。

九. 职责链的缺点

让我们继续回到上面的例子,我们发现,其实当请假时间超过2天的时候,PM和HR其实没有做任何的事情,而只是做了一个传递工作。

而传递工作之后,他们就成了垃圾对象。

也就是说,他们在实际的处理中,并没有发挥任何的作用。

那么当这个链结构比较长,比较复杂的话,会产生很多的内存垃圾对象。

这也就是职责链的最大缺点之所在。

十. 职责链的乱用

在和其他的人的讨论中,我发现他们的观点是:

只要一者传一者,那么就要用职责链。在我们的项目中,他们这样去用:

abstract class DBHelper
{ 

}

interface IRequestHandler
{
    IDBHelper ReturnHelper(string dbName);
}
class RequestHandler:IRequestHandler
{
    private RequestHandler successor;
    public RequestHandler Successor
    {
        get { return successor; }
        set { successor = value; }
    }
    public abstract IDBHelper ReturnHelper(string dbName);
}

class SQLHelper : DBHelper
{ 

}
class OracleHelper : DBHelper
{ 

}
class DB2Helper : DBHelper
{ 

}
class SQL : RequestHandler
{
    public override IDBHelper ReturnHelper(string dbName)
    {
        if (dbName.Equals("SQL Server"))
        {
            return new SQLHelper();
        }
        return Successor.ReturnHelper(dbName);
    }
}
class Oracle : RequestHandler
{
    public override IDBHelper ReturnHelper(string dbName)
    {
        if (dbName.Equals("Oracle"))
        {
            return new OracleHelper();
        }
        return Successor.ReturnHelper(dbName);
    }
}
class DB2 : RequestHandler
{
    public override IDBHelper ReturnHelper(string dbName)
    {
        if (dbName.Equals("DB2"))
        {
            return new DB2Helper();
        }
        return new SQLHelper();
    }
}

这样的话,每个类相当于只负责一个操作。

那么我们如何改进呢?第一,我们可以用一个工厂来实现。另外,我们可以用表驱动的方式来解决问题。

十一. 表驱动改进职责链

表驱动(Table driven),其实就是指用查表的方式来获取值。

那么我们用标驱动法来改进上面的例子:

class HelperRequest
{
    private Dictionary<String, DBHelper> dic = new Dictionary<string, DBHelper>();
    public void Add(string name,DBHelper helper)
    {
        dic.Add(name, helper);
    }
    public DBHelper GetHelper(string name)
    {
        DBHelper helper;
        bool temp = dic.TryGetValue(name, out helper);
        if (temp)
        {
            return helper;
        }
        return null;
    }
}

我想一个没有学过设计模式的人都会这样写的。一个学过设计模式很多年的人也会这样写的。

而怕的就是为了模式而模式,为了职责链而职责链了。

十二. 职责链在java script中的应用

我们想象这样一种情况:

我们都知道,在ASP.NET 的 Webform模型中页面是以控件树的形式去组织的。那么我们用右键点击其中的一个页面,那么这个事件就会找离他最近的控件,如果不存在,那么就去找他的父控件,如此递归下去,直到找到为止。

这其实就是一种职责链的体现!

十三. 深析职责链的使用

职责链模式不能乱用,否则非常容易变成因为模式而模式的反例。

下面是我归纳出来的一些关于职责链方面的使用规则,只是个人的意见,还希望大家指教。

1, 如果存在N对N,或者是一般的常规线性关系,那么我们完全可以用表驱动来取代职责链。

2, 对象本身要经过什么处理是通过每个链上元素通过运行态来决定的,决定的因素是取决于对象的属性或者一些其他方面的策略。

3, 用户无论是从哪一个节点作为他的请求头节点,最终用户都可以得到一个请求的反馈。

4, 应怪怪建议,补充同级的处理!职责链并非是严格的上下级的传递,其中也包括同级的传递,职责链一样可以在同级之间做传递。

例如,继续用我们上面请假的那个做例子,也许我们公司有两个HR,事实上也是这样的,我们把前台“MM”也美称为人力资源部:

static void Main(string[] args)
{
    Request request = new Request(3, "非正当理由");
    Boss pm = new PM("pm");
    Boss hr1 = new HR("Real HR");
    Boss hr2 = new HR("QiantaiMM");
    Boss manager = new Manager("manager");
    pm.Successor = hr1;
    hr1.Successor = hr2;
    hr2.Successor = manager;
    bool pass = pm.PassRequest(request);
    Console.Write(pass);
}

其实这样也未尝不可。有人也许会说,那么这样的同样一个类的两个对象又有什么意义呢?

那么我们不妨去试着这样改造这个HR的类。

enum HRType
{
    RealHR,
    Qiantai
}
class HR:Boss
{
    private HRType type;
    public HR(string name,HRType type)
        : base(name)
    {
        this.type = type;
    }
    public override bool PassRequest(Request request)
    {
        int day = request.Day;
        if (day>=0.5&&day<2)
        {
            switch (type)
            {
                case HRType.RealHR:
                    //扣工资
                    return true;
                    break;
                case HRType.Qiantai:
                    //不扣工资
                    return true;
                    break;
            }
        }
        return Successor.PassRequest(request);
    }
}

这样,因为前台MM容易说话,很可能他就不去扣你的工资,如果你去先找的HR,那么你这天的工资就报销了。

同理,我们一样可以让他们的职责细化,比如说Real Hr负责0.5天到1天的,而Qiantai去负责1天到2天的,也未尝不可。

总之,职责链并非是单一的上下级的传递,一样可以实现同级的传递。

十四. 职责链总结

1、Chain of Responsibility 模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
2、应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
3、如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。

时间: 2024-09-13 13:57:21

职责链模式(chain of responsibility)的相关文章

设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为)

 设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述        你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象).至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,并且沿着这条链传递请求,直到有(部门)对象处理它为止. 例子1:js的事件浮升机制 例子2: 2.问题

重温设计模式(三)——职责链模式(chain of responsibility)

一.写在前面的 这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式. 因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理.所以希望各位多多指教. 二.什么是链 文章伊始,先让我们了解这个最基本的概念,什么是链. 我给链下了这样的定义: 1.链是一系列节点的集合. 2.链的各节点可灵活拆分再重组. 三.何为职责链 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着

乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern)

原文:乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern)[索引页][源码下载] 乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern) 作者:webabcd 介绍 为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它. 示例 有一个Message实体类,某个类对它的操作有Insert()方法.现在要求根据插

php Chain of Responsibility 职责链模式

比如一颗原子弹投下的瞬间,在杀伤范围内的部队或者建筑都会减少血,但是随着距离中心点的远近,受损程度是不同的,而且不同的兵种和建筑受损情况是不同的. 待解决的问题:原子弹投下的瞬间,将杀伤的处理分别交给杀伤范围内的部队或者建筑自己的方法处理. 思路:建立一个接口,让所有的部队或者建筑实现.   职责链模式(Chain of Responsibility)示例:  代码如下 复制代码 <?php //被原子弹攻击的接口 interface NuclearAttacked {     //处理被原子弹

设计模式学习笔记(十九)—Chain of Responsibility职责链模式

由于本人水平有限,写出来的东西也许对初学者有所帮助.如果不小心哪位大侠看了不要见笑,哪里有不正确的地方还请批评指正.好了不说废话了. Chain of Responsibility模式定义: 为了避免请求的发送者和接收者之间的耦合关系,使多个接受对象都有机会处理请求.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 我的理解: 在不止一个对象可以处理客户端请求的时候,为了使每个对象都有处理请求的机会,把这些对象顺序地串联起来形成一个链,每个被串联的对象都有一个指向下一个对

第22章 职责链模式(Chain of Responsibility)

原文 第22章 职责链模式(Chain of Responsibility) 职责链模式        导读:职责链模式是一个既简单又复杂的设计模式,刚开始学习这个设计模式的时候光示例都看了好几遍.就为了理清里面的逻辑走向.是个值得慢慢品味的设计模式        概述:        使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止.          结构图:          代码举例:公司请假

php设计模式 Chain Of Responsibility (职责链模式)

复制代码 代码如下: <?php /** * 职责链模式 * * 为解除请求的发送者和接收者之间的耦合,而使用多个对象都用机会处理这个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它 * */ abstract class Handler { protected $_handler = null; public function setSuccessor($handler) { $this->_handler = $handler; } abstract functio

php设计模式 Chain Of Responsibility (职责链模式)_php技巧

复制代码 代码如下: <?php /** * 职责链模式 * * 为解除请求的发送者和接收者之间的耦合,而使用多个对象都用机会处理这个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它 * */ abstract class Handler { protected $_handler = null; public function setSuccessor($handler) { $this->_handler = $handler; } abstract functio

基于.NET的职责链模式类库——NChain

Chain.NET(又名NChain)是职责链模式在.NET和Mono平台上的一个实现.它的0.1版本(已可以在SourceForge中访问)结合了标准的职责链模式以及命令模式,目的是"为基于命令处理的功能提供一个方便而又灵活的解决方案". NChain松散地基于Java平台上的Jakarta的Commons Chain包.一般说来,职责链模式从一系列处理单元中分解出命令对象从而解耦.每个处理单元均包含相应的代码,以描述它可接受的命令对象的类型:另外它还会委托部分责任,用来处理与职责链