设计模式专题(二十) ——职责链模式

设计模式专题(二十)——职责链模式

(原创内容,转载请注明来源,谢谢)

一、概述

职责链模式(Chainof Responsibility),是使多个对象都有机会处理请求,从而避免请求的发送者和接收者直接的耦合。将这个对象练成一条链,并且沿着这个链传递该请求,直到有一个对象处理它为止。

客户端发出的请求,如果对于不同的量级、不同的内容,都需要有不同的类进行处理,则可以使用职责链模式。例如员工的请假,不同的请假天数要不同的人来审批;请假的类型不同处理的方式也有所不同。

二、类图

从图中可以看出,由客户端发送请求后,统一发送给handler类,handler类会根据请求的类型(如判断请假的是事假还是病假),发送给不同的具体处理类。处理类如果发现请求的内容超出范围(如请假天数是3天还是10天),则会将请求转发给下一个具体处理类。下一个处理类处理完毕,会将消息直接返回给handler类,由handler返回给客户端。

该模式中,还需要一个默认处理类,将超出一定范围、请求类型不在处理范围的,做一个兼容的处理。

三、实现方式

1、场景

现以请假作为场景,当不同的请假类型、不同的请假天数,假设需要不同的类去进行处理。

由客户端发出请假请求,标明请假类型和天数给统一的handler。

由handler根据类型进行分发给最底层级的各请假类型的处理类,各类再根据具体的请假天数,判断是自己处理还是给高一层级的类进行处理。

2、常量类

为了便于判断不同的请假类型,现设定常量类,用于存放各类请假类型,避免程序中出现请假类型,也让代码更佳优雅。常量类如下:

class LeaveConst{
         publicstatic const ANNUAL_LEAVE = 0;
         publicstatic const SICK_LEAVE = 1;
         publicstatic const COMPASSION_LEAVE = 2;
         publicstatic const MARRIAGE_LEAVE = 3;
         publicstatic const MATERNITY_LEAVE = 4;
         publicstatic const OTHER_LEAVE = 5;
         publicstatic const $TypeDef = array(
                   self::ANNUAL_LEAVE=> '年假',
                   self::SICK_LEAVE=> '病假',
                   self::COMPASSION_LEAVE=> '事假',
                   self::MARRIAGE_LEAVE=>'婚假',
                   self::MATERNITY_LEAVE=> '产假',
                   self::OTHER_LEAVE=> '其他'
         );
         publicstatic const $TypeEnglishDef = array(
                   0=> 'ANNUAL_LEAVE',
                   1=> 'SICK_LEAVE',
                   2=> 'COMPASSION_LEAVE',
                   3=> 'MARRIAGE_LEAVE',
                   4=> 'MATERNITY_LEAVE',
                   5=> 'OTHER_LEAVE'
         );
         publicstatic const $TypeIn = array(
                   self::ANNUAL_LEAVE,
                   self::SICK_LEAVE,
                   self::COMPASSION_LEAVE,
                   self::MARRIAGE_LEAVE,
                   self::MATERNITY_LEAVE,
                   self::OTHER_LEAVE
         );
}

3、Handler处理类

该类起到中间的作用,针对客户端的请求的类型进行任务分发,并返回处理结果。

class Handler{
         private$handlerType;
         private$type;
         private$days;
         //设定类型和内容,并自动执行结果
         publicfunction setHandler($type, $days){
                   if(!in_array($type,LeaveConst::$TypeIn) || empty($days) || !is_numeric($days)){
                            returnfalse;
                   }
                   $this->type= $type;
                   $this->days= $days;
                   return$this->execute();
         }
         //执行请求
         privatefunction execute(){
                   //截取出_的前半段,即为类型
                   $arrType= explode('_', LeaveConst::$TypeEnglishDef($this->type));
                   //拼接出具体类名
                   $handlerType= $arrType[0] . 'HandlerA';
                   //获取具体类的实例
                   $this->handlerType= $handlerType;
                   //执行具体类的请求
                   return$this->handlerType::execute($this->days);
         }
}

4、具体的处理类

具体的处理类,通过handler类的分发,进行处理。如果其具备处理能力,则处理后返回结果,否则将对更高层级的处理类发送处理请求。

下面共有三个类。

class AnnualHandlerA{
         publicstatic function execute($days){
                   if(empty($days)|| !is_numeric($days)){
                            returnfalse;
                   }
                   if($days< 3){
                            /*
 小于3时有权限,则直接处理
 具体的处理过程
                            */
                            return'处理结果';
                   }else{
                            returnAnnualHandlerB::execute($days);
                   }
         }
}
class AnnualHandlerB{
         publicstatic function execute($days){
                   if(empty($days)|| !is_numeric($days) || $days<3){
                            //如果小于3,不应由该类进行处理,则报错
                            returnfalse;
                   }
                   if($days< 10){
                            /*
 小于10时有权限,则直接处理
                              ...具体的处理过程
                            */
                            return'处理结果';
                   }else{
                            returnAnnualHandlerDefault::execute($days);
                   }
         }       
}
class AnnualHandlerDefault{
         publicstatic function execute($days){
                   if(empty($days)|| !is_numeric($days) || $days<10){
                            //如果小于10,不应由该类进行处理,则报错 
                            returnfalse;
                   }
                   /*
 默认的处理过程,在前面的处理条件都不符合的情况下,
 由该类进行处理,实现兼容
                     ...具体的处理过程
                   */
                   return'处理结果';
         }       
}

5、客户端发送请求

$annualLeave = new Handler();
$annualLeave->setHandler(LeaveConst::ANNUAL_LEAVE, 20);

四、总结

职责链模式,实现请求的统一分发,并且根据不同的请求规模,具体的处理类进行判断和转发,使客户端和请求处理端解耦,也使请求的分发模块和请求的具体实现模块解耦。

该模式下,对请求模式的增删改、对请求规模的增删改,都可以很容易的实现,且不会影响到其他的类。

——written by linhxx 2107.08.28

相关阅读:

设计模式专题(十九) ——命令模式

设计模式专题(十八) ——桥接模式

设计模式专题(十七) ——单例模式

设计模式专题(十六)——迭代器模式

设计模式专题(十五) ——组合模式

设计模式专题(十四)——适配器模式

设计模式专题(十三) ——备忘录模式

设计模式专题(十二)——状态模式

设计模式专题(十一)——抽象工厂模式

设计模式专题(十)——观察者模式

设计模式专题(九) ——外观模式

设计模式专题(八) ——模板方法模式

设计模式专题(七)——建造者模式

设计模式专题(六)——原型模式

设计模式专题(五)——工厂方法模式

设计模式专题(四)——代理模式

设计模式专题(三)——装饰模式

设计模式专题(二)——策略模式

设计模式专题(一)——面向对象的设计原则

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-08-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏wblearn

简书搜索自动匹配功能

今天周六,我又来分享知识啦。最近一直在忙项目,所以趁着这个周末,喝着咖啡,听着音乐,敲着代码就把做项目的知识点总结给大家,简直不要太惬意,哈哈。

531
来自专栏Java帮帮-微信公众号-技术文章全总结

面试复习大纲(最全面)

Java基础 1.数组中的排序问题(笔试或者机试,前者可能性更大) 2.面向对象的理解 面向对象主要有四个特性: 封装、抽象、继承和多态。 封装:在面向对象语言...

3285
来自专栏牛客网

爱奇艺现场面试总结

一面: java内存模型:问面试官是jmm还是jvm,说是jvm,说了说分区 堆内存:说了堆内存划分和理由,各种内存的分配流程,各类回收算法。 项目:略 mys...

4627
来自专栏coding for love

在线商城项目14-阶段性自测与bug修复

前面把商品列表页的查询展示逻辑基本完成了。每个功能单独测试是没有问题了,但是连在一起呢?新增的功能是否会对以前的功能产生影响。

693
来自专栏python爬虫实战之路

12306自动刷票下单-下单

进入下单界面了 https://kyfw.12306.cn/otn/confirmPassenger/initDc

1135
来自专栏逸鹏说道

C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子)

第一次接触HtmlAgilityPack是在5年前,一些意外,让我从技术部门临时调到销售部门,负责建立一些流程和寻找潜在客户,最后在阿里巴巴找到了很多客户信息,...

3168
来自专栏马洪彪

C#.Net与MATLAB集成

在数学分析工具方面,MATLAB无疑是佼佼者,除了作为软件工具外,MATLAB的自定义编程语言以及混合编程的支持,使其可以与Python、R之类数学分析语言媲美...

2777
来自专栏斑斓

【编码修炼】ScalaTest的测试风格

ScalaTest几乎已经成为Scala语言默认的测试框架,而在JVM平台下,无论是否使用Scala进行开发,我认为仍有尝试ScalaTest的必要。这主要源于...

3427
来自专栏小樱的经验随笔

Free Pascal初次体验(有亮点哦)

感觉上Pascal语言写的非常有条理,和英语很像,应该是比较容易学,但是写起来真的是麻烦的要死,平时一行代码用C/C++可能就是几秒钟,用Pascal就要几分钟...

2635
来自专栏web编程技术分享

《从案例中学习JavaScript》之酷炫音乐播放器(二)

33413

扫码关注云+社区