重温设计模式(三)——职责链模式(chain of responsibility)
一. 写在前面的
这么多的设计模式,我觉得职责链是我第一次看上去最简单,可是回想起来却又最复杂的一个模式。
因此,这个文章我酝酿了很久,一直也没有胆量发出来,例子也是改了又改,可是仍然觉得不够合理。所以希望各位多多指教。
二. 什么是链
![]()
文章伊始,先让我们了解这个最基本的概念,什么是链。
我给链下了这样的定义:
1. 链是一系列节点的集合。
2. 链的各节点可灵活拆分再重组。
三. 何为职责链
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
图如下:
![]()
UML很简单,让我们先来看一个简单的例子。
四. 职责链模式应用之请假管理
请假这个事情,相信每个人都不陌生。
我们公司是个相对很宽松的公司。
在公司里,如果你的请假时间小于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天的,也未尝不可。
总之,职责链并非是单一的上下级的传递,一样可以实现同级的传递。
十四. 职责链总结
职责链是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
今天就写到这了,希望大家多多指教。
原文:http://www.cnblogs.com/kym/archive/2009/04/06/1430078.html
重温设计模式(三)——职责链模式(chain of responsibility)的更多相关文章
- 设计模式之职责链模式(Chain of Responsibility)摘录
23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...
- 行为型设计模式之职责链模式(Chain of Responsibility)
结构 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 适用性 有多个的对象可以处理一个请求,哪个 ...
- 设计模式之职责链模式(Chain of Responsibility)
职责链模式原理: 职责链模式和装饰模式以及组合模式类似的地方是都维持着指向父类的指针, 不同点是职责链模式每个子类都继承父类的指针及每个之类都维持着指向父类的指针,而组合模式与装饰模式是组合类鱼装饰类 ...
- atitit.设计模式(1)--—职责链模式(chain of responsibility)最佳实践O7 日期转换
atitit.设计模式(1)---职责链模式(chain of responsibility)最佳实践O7 日期转换 1. 需求:::日期转换 1 2. 可以选择的模式: 表格模式,责任链模式 1 3 ...
- 责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)
责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这 ...
- 设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型)
设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就 ...
- 设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为)
设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决.不能解决就 ...
- 职责链模式(Chain of Responsibility)(对象行为型)
1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象).至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与 ...
- 乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern)
原文:乐在其中设计模式(C#) - 责任链模式(Chain of Responsibility Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 责任链模式(Chain of R ...
- 设计模式:职责链模式(Chain of Responsibility)
去年参加校招要到长沙来,这个对于我来说不是特别喜欢(但又必须的来,谁叫咱不是985.211的娃呢),但是对于某些人来说就是福音了.大四还有课,而且学校抓的比较严,所以对于那些想翘课的人来说这个是最好不 ...
随机推荐
- chrome扩展
chrome拓展开发实战:页面脚本的拦截注入 时间 2015-07-24 11:15:00 博客园精华区 原文 http://www.cnblogs.com/horve/p/4672890.htm ...
- JQuery学习笔记——JQuery基础
#,JQuery避免名称冲突的方法 var jq = jQuery.noConfilct(); jq.ready( function(){ jq("p").hidden() ...
- [CareerCup] 9.10 Stack Boxes 垒箱子问题
9.10 You have a stack of n boxes, with widths w., heights hir and depths drThe boxes cannot be rotat ...
- 学习笔记——Maven实战(二)POM重构之增还是删
重构是广大开发者再熟悉不过的技术,在Martin Fowler的<重构——改善既有代码的设计>一书中,其定义为“重构(名词):对软件内部结构的一种调整,目的是在不改变软件之可察行为前提下, ...
- php利用递归函数实现无限级分类
相信很多学php的很多小伙伴都会尝试做一个网上商城作为提升自己技术的一种途径.各种对商品分类,商品名之类的操作应该是得心应手,那么就可以尝试下无限级分类列表的制作了. 什么是无限级分类? 无限级分类是 ...
- SQL Server 2008 过期
最近SQL Serve 2008 过期,不能正常运行,通过如下方式解决此问题: (1) 修改注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQ ...
- 文件夹文件遍历并插入数据库的操作,IO Directory File的递归操作
在我们管理内容管理系统时,数据量大时,对机器的依赖性就比较强了,比如,我要将一个文件夹中的很多图片上传到网站,一个个上传会很花时间,就想到了通过遍历文件夹得到文件名,并将路径与文件保存到数据库中对应的 ...
- 编写高质量代码改善C#程序的157个建议[匿名类型、Lambda、延迟求值和主动求值]
前言 从.NET3.0开始,C#开始一直支持一个新特性:匿名类型.匿名类型由var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 1.既支持简单类型也支持复杂类 ...
- EntityFramework_MVC4中EF5 新手入门教程之七 ---7.通过 Entity Framework 处理并发
在以前的两个教程你对关联数据进行了操作.本教程展示如何处理并发性.您将创建工作与各Department实体的 web 页和页,编辑和删除Department实体将处理并发错误.下面的插图显示索引和删除 ...
- oracle-2中commit 详解
博文转自:http://blog.csdn.net/hzhsan/article/details/9719307 它执行的时候,你不会有什么感觉.commit在数据库编程的时候很常用,当你执行DML操 ...