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

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

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

一、概述

职责链模式(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 条评论
登录 后参与评论

相关文章

来自专栏代码世界

Python之装饰器

函数篇--装饰器 装饰器的主要功能: 一个闭包函数。 装饰器的主要功能: 在不改变函数调用方式的基础上在函数的前、后添加功能。 开放封闭原则: 1.对扩展是开放...

2708
来自专栏一“技”之长

iOS中CoreData数据管理系列一——初识CoreData

    CoreData是一个专门用来管理数据的框架,其在性能与书写方便上都有很大的优势,在数据库管理方面,apple强烈推荐开发者使用CoreData框架,在...

463
来自专栏杨建荣的学习笔记

通过Java程序测试数据库连接信息 (r10笔记第64天)

很就没写Java了,今天简单问了下行情,如今都是Java 9的时代了,老系统基本上都是在Java 7。 Oracle中很早就糅合了Java,Oracle 10g...

3427
来自专栏林德熙的博客

WPF 判断调用方法堆栈

最近遇到一个问题,经常有小伙伴在类A的构造里调用静态函数B,但是这时B依赖于A的初始化完成,于是就无限循环。所以我需要在判断小伙伴调用B时是否在A的构造方法里,...

281
来自专栏技术点滴

桥接模式(Bridge)

桥接模式(Bridge) 桥接模式(Bridge)[Handle/Body] 意图:将抽象部分与它的实现部分分离,使他们都可以独立的变化。 应用:不同系统平台的...

1746
来自专栏喵了个咪的博客空间

zephir-(12)php函数和异常处理

#zephir-php函数和异常处理# ? ##前言## 先在这里感谢各位zephir开源技术提供者 经过了一个多月的学习,zephir的文档译文和基础讲解也将...

3326
来自专栏后端之路

JMockit原理剖析

上篇文章描述了Jmockit的最基本的也是最方便也是最神奇的mock神技单元测试JMockit使用 本篇大概就其原理好好说道。 背景知识 Instrumenta...

4259
来自专栏微服务生态

由学习《软件设计重构》所想到的代码review(二)

我们接第一篇来继续说明在代码review中,有哪些属于“层次结构”中的坏味道。 第一篇链接如下:http://www.jianshu.com/p/07dbf6...

642
来自专栏青玉伏案

代码重构(五):继承关系重构规则

陆陆续续的发表了多篇关于重构的文章了,还是那句话,重构是一个项目迭代开发中必不可少的一个阶段。其实重构伴随着你的项目的整个阶段。在前几篇关于重构的文章中我们谈到...

1796
来自专栏张善友的专栏

Clay: 创建和使用深层次对象图

Clay 是 CodePlex 上的一个开源项目,帮助我们创建轻松创建对象,就 JavaScript 或其它动态语言一样简单。Clay 项目的网址是 http:...

1766

扫描关注云+社区