设计模式-职责链模式在Java中使用示例-采购审批系统

news2024/11/24 8:25:56

场景

采购单分级审批

采购审批是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批,主任可以审批5万元以下(不包括5万元)

的采购单,副董事长可以审批5万元至10万元(不包括10万元)的采购单,董事长可以审批10万元至50万元(不包括50万元)

的采购单,50万元及以上的采购单就需要开董事会讨论决定。

如果不采购职责链模式可能会这样实现

public class PurchaseRequestHandler {
    public void sendRequestToDirector(int amount){
        if(amount<5000){
            this.handleByDirector();
        }else if(amount<10000){
            this.handleByVicePresident();
        }else if(amount<50000){
            this.handleByPresident();
        }else {
            this.handleByCongress();
        }
    }

    //主任审批采购单
    public void handleByDirector(){
        //代码省略
    }

    //副董事长审批采购单
    public void handleByVicePresident(){
        //代码省略
    }

    //董事长审批采购单
    public void handleByPresident(){
        //代码省略
    }

    //董事会审批采购单
    public void handleByCongress(){
        //代码省略
    }

}

存在问题

(1)PurchaseRequestHandler类较为庞大,各个级别的审批方法都集中在一个类中,违反了“单一职责原则”,

测试和维护难度大。

(2)如果需要增加一个新的审批级别或调整任何一级的审批金额和审批细节(例如将董事长的审批额度改为60万元)

时都必须修改源代码并进行严格测试,此外,如果需要移除某一级别(例如金额为10万元及以上的采购单直接由董事长审批,

不再设副董事长一职)时也必须对源代码进行修改,违反了“开闭原则”。

(3)审批流程的设置缺乏灵活性,现在的审批流程是“主任—>副董事长—>董事长—>董事会”,如果需要

改为“主任—>董事长—>董事会”,在此方案中只能通过修改源代码来实现,客户端无法定制审批流程。

职责链模式

职责链模式(Chain of Responsibility Pattern):

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,

直到有对象处理它为止。职责链模式是一种对象行为型模式。

职责链模式结构图

职责链模式角色

Handler(抽象处理者):

它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,

因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了

一个抽象处理者类型的对象(如结构图中的successor),作为其对下家的引用。通过该引用,处理者可以连成一条链。

ConcreteHandler(具体处理者):

它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,

在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;

在具体处理者中可以访问链中下一个对象,以便请求的转发。在职责链模式里,很多对象由每一个对象对其下家

的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的

客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

注:

博客:
霸道流氓气质_C#,架构之路,SpringBoot-CSDN博客

实现

以上采购审批系统用职责链模式实现

1、新建采购单对象,作为请求类

import lombok.Data;

//采购单:请求类
@Data
public class PurchaseRequest {

    //采购金额
    private double amount;
    //采购单编号
    private int number;
    //采购目的
    private String purpose;

    public PurchaseRequest(double amount, int number, String purpose) {
        this.amount = amount;
        this.number = number;
        this.purpose = purpose;
    }

}

2、新建审批者类,作为抽象处理者

//审批者类:抽象处理者
abstract class Approver {
    //定义后继对象
    protected Approver successor;
    //审批者姓名
    protected String name;

    public Approver(String name){
        this.name = name;
    }

    //设置后继者
    public void setSuccessor(Approver successor){
        this.successor = successor;
    }

    //抽象处理请求方法
    public abstract void processRequest(PurchaseRequest request);
}

3、新建主任类,作为具体处理者

//主任类:具体处理者
public class Director extends Approver {

    public Director(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<50000){
            System.out.println("主任"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

4、同样新建副董事长类

//副董事长类:具体处理者
public class VicePresident extends Approver {

    public VicePresident(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<100000){
            System.out.println("副董事长"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

5、同样新建董事长类

//董事长类:具体处理者
public class President extends Approver {

    public President(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<500000){
            System.out.println("董事长"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

6、同样新建董事会类

//董事会类:具体处理者
public class Congress extends Approver {

    public Congress(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        System.out.println("董事会"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
    }
}

7、客户端调用方式

public class Client {
    public static void main(String[] args) {

        Approver zhuren,fudongshi,dongshi,dongshihui;
        zhuren = new Director("张主任");
        fudongshi = new VicePresident("李副董事长");
        dongshi = new President("王董事长");
        dongshihui = new Congress("董事会");

        //创建职责链
        zhuren.setSuccessor(fudongshi);
        fudongshi.setSuccessor(dongshi);
        dongshi.setSuccessor(dongshihui);

        //创建采购单
        PurchaseRequest pr1 = new PurchaseRequest(49000,1001,"购买网线");
        zhuren.processRequest(pr1);

        PurchaseRequest pr2 = new PurchaseRequest(89000,1002,"购买服务器");
        zhuren.processRequest(pr2);

        PurchaseRequest pr3 = new PurchaseRequest(190000,1003,"购买机柜");
        zhuren.processRequest(pr3);

        PurchaseRequest pr4 = new PurchaseRequest(600000,1004,"购买机房");
        zhuren.processRequest(pr4);

    }
}

8、总结

如果需要在系统增加一个新的具体处理者,如增加一个经理(Manager)角色可以审批5万元至8万元(不包括8万元)的采购单,

需要编写一个新的具体处理者类Manager,作为抽象处理者类Approver的子类,实现在Approver类中定义的抽象处理方法,

如果采购金额大于等于8万元,则将请求转发给下家。

职责链模式的主要优点:

(1) 职责链模式使得一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,

接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构,由客户端负责链的创建,

降低了系统的耦合度。

(2) 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,

可简化对象的相互连接。

(3) 在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加

或修改来增加或改变处理一个请求的职责。

(4) 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,

从这一点来看是符合“开闭原则”的。

职责链模式的主要缺点:

(1) 由于一个请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;

一个请求也可能因职责链没有被正确配置而得不到处理。

(2) 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

(3) 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。

适用场景:

(1) 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,

而无须关心请求的处理对象是谁以及它是如何处理的。

(2) 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

(3) 可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理者之间的先后次序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/785655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于C语言的哈夫曼转化软件

完整资料进入【数字空间】查看——baidu搜索"writebug" 该软件可实现如下功能&#xff1a; 用户可以通过点击“打开文件”按钮打开本地计算机中任意一个文本文件&#xff0c;点击确认将其文本导入到程序输入文本框中&#xff0c;也可以直接在文本框中通过键盘键入文…

502 Bad GateWay报错的解决方法

什么是502 bad gateway 报错 简单来说 502 是报错类型代码 bad gateway 错误的网关。是Web服务器作为网关或代理服务器时收到无效的响应。 用我们的口语说就是运行网站的服务器暂时挂了(不响应)。 产生错误的原因 1.连接超时 我们向服务器发送请求 由于服务器当前链接太多&am…

js设置一个定时器,定时发送请求

1.setTimeout方式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </he…

整流电路设计

一、交直流电区别 交流电&#xff1a;一般指大小和方向都随时间作周期性变化的电压或电流。交流电的极性在一个周期内变换多次&#xff0c;而直流电则保持恒定。 直流电&#xff08;电压或电流&#xff09;&#xff1a;直流电的方向不随时间而变化。直流电通常又分为恒定电压…

【Linux命令200例】chmod设置文件或目录的访问权限

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

Python获取接口数据

首先我们需要下载python&#xff0c;我下载的是官方最新的版本 3.8.3 其次我们需要一个运行Python的环境&#xff0c;我用的是pychram&#xff0c;需要库的话我们可以直接在setting里面安装 代码&#xff1a; # -*- codeing utf-8 -*- from bs4 import BeautifulSoup # 网页…

Stable Diffusion生成艺术二维码

Stable Diffusion生成艺术二维码 文章会有浏览问题&#xff0c;点击此处查看原文 首先需要一个Stable Diffusion服务环境&#xff0c;《Stable Diffusion服务环境搭建&#xff08;远程服务版&#xff09;》如果你已经有了那就忽略 一、准备一个比较好的二维码底图 首先解析二…

第一章 介绍 对 HL7 版本 2 的支持

文章目录 第一章 介绍InterSystems 对 HL7 版本 2 的支持HL7 版本 2 路由生成 第一章 介绍 InterSystems 对 HL7 版本 2 的支持 InterSystems 产品支持 HL7 版本 2 消息作为虚拟文档。虚拟文档是 InterSystems 产品仅部分解析的一种消息。这种消息具有标准的production 消息头…

微信朋友圈推广优势有哪些?

你知道的朋友圈推广效果有哪些 1、定向精准触达 在朋友圈广告推广中&#xff0c;人群的性别、年龄、兴趣爱好、活动范围、学历、手机型号、推广时段等&#xff0c;这些都是可以进行精准定向。通过精准定向&#xff0c;可以找到企业想找到的人群。 2、用户高活跃度 大家平均每…

亿发数字化生产工厂MES管理系统,助力云南工厂实现智能制造

近年来&#xff0c;云南省将制造业数字化作为“工业强省”战略的关键和打造现代产业体系的重中之重&#xff0c;而随着工业4.0时代的来临&#xff0c;数字化转型已成为制造企业企业蓬勃发展之路。在这个过程中&#xff0c;MES精益制造管理系统显露出潜力&#xff0c;成为云南省…

前端生成批量二维码,并且下载到本地

Ⅰ- 壹 - 功能展示和使用需求 需求描述 前端生成批量二维码&#xff0c;并且下载&#xff0c;本项目使用了 vue3. 功能展示 Ⅱ - 贰 - 封装代码 需要的库 yanr add qrcodejs2-fix // 生成二维码 yarn add html2canvas // 转图片 yarn add jszip// 压缩包 yarn add file-sa…

25.2 matlab里面的10中优化方法介绍——插值法(matlab程序)

1.简述 插值法 插值法又称“内插法”&#xff0c;是利用函数f (x)在某区间中已知的若干点的函数值&#xff0c;作出适当的特定函数&#xff0c;在区间的其他点上用这特定函数的值作为函数f (x)的近似值&#xff0c;这种方法称为插值法。如果这特定函数是多项式&#xff0c;就称…

(css)列表点击前后样式

(css)列表点击前后样式 效果&#xff1a; html <ul v-show"rightOne" class"one-content"><liv-for"(item,index) in exampleList":key"index"click"searchHandle(item,index)"class"liClass":class&qu…

Linux基本指令操作

登陆指令&#xff08;云服务器版&#xff09; 当我们获取公网IP地址后&#xff0c;我们就可以打开xshell。 此时会有这样的界面&#xff0c;我们若是想的登陆&#xff0c;则需要输入以下的指令 ssh 用户名公网IP地址 然后会跳出以下的窗口 接着输入密码——密码便是先前定好…

ArcGIS Engine 与 Visual Studio版本对照表

通过C#对于Arcgis的二次开发&#xff0c;需要Visual Studio版本需要与ArcGIS Engine对应&#xff0c;Visual Studio版本的或高或低都不能使ArcObjects SDK for microsoft.Net framework安装成功。下面是各个版本的对照表。 序号ArcEngine版本visual Studio版本Network版本110.…

React Native实现理想的震动效果

React Native实现理想的震动效果 一、背景说明 业务开发中&#xff0c;总会用到一些和用户反馈的效果&#xff0c;用来提升用户对于某个事件或者操作的重要程度&#xff0c;比如常见的就是 长按复制、滑动或点击图表、点击底部TabBar时的反馈等操作。 二、构思实现及过程 2.…

微信小游戏个人开发者上架:从注册到上线的详细步骤

微信小游戏个人开发者上架&#xff1a;从注册到上线的详细步骤 一&#xff0c;注册小程序账号1.1 微信公众平台1.2 填写信息1.3 绑定管理 二&#xff0c;打包步骤2.1 工具准备2.2 关于Unity版本2.3 打包详解 三&#xff0c;提包步骤3.1 填写用户隐私3.2 完善开发者自查3.3 游戏…

《向量数据库指南》:向量数据库Pinecone使用命名空间

目录 创建命名空间 创建多个命名空间 跨所有命名空间的操作 Pinecone允许您将索引中的向量划分为命名空间。然后,查询和其他操作仅限于一个命名空间,因此不同的请求可以搜索索引的不同子集。 例如,您可能想为按内容索引的文章定义一个命名空间,为按标题索引的文章定义另…

内存条的故障修复方法分享

你知道电脑的内存条遇到故障要怎么修复吗&#xff1f;可能很多小伙伴都是直接说&#xff1a;“找电脑维修师傅就好。”那当只有你自己的时候怎么办呢&#xff1f;今天我就跟你分享几个电脑常见的故障以及修复的方法吧。 1、开机无显示。此类故障通常是由于内存条与主板内存插槽…

kernel-pwn之ret2dir利用技巧

前言 ret2dir是2014年在USENIX发表的一篇论文&#xff0c;该论文提出针对ret2usr提出的SMEP、SMAP等保护的绕过。全称为return-to-direct-mapped memory&#xff0c;返回直接映射的内存。论文地址&#xff1a;https://www.usenix.org/system/files/conference/usenixsecurity1…