首页 > 其他分享 >设计模式---责任链模式

设计模式---责任链模式

时间:2022-11-09 22:33:51浏览次数:37  
标签:责任 Chain void request next --- new 设计模式 public

简述

将各个功能拆分后分别封装(各功能解耦),需要时可自由组合(包括执行顺序)

话不多说,看个优化案例吧。

优化案例

最初版

以下是模拟客户端想服务端发送请求的业务流程。

客户端调用代码如下。

// 客户端
public class Client {
    public static void main(String[] args) {
        Map<String, String> request = new HashMap<>();
        request.put("username", "admin");
        request.put("password", "admin");
        service(request);
    }
    public static void service(Map<String, String> request) {
        String username = request.get("username");
        String password = request.get("password");
        if (!"admin".equals(username) || !"admin".equals(password)) {
            throw new RuntimeException("用户名或密码不正确!");
        }
        System.out.println("用户认证通过。");
        System.out.println("正在处理请求的业务。");
    }
}

如果根据新的需求,需要在用户认证通过到处理请求的业务之间增加一个缓存用户信息至Session的处理该怎么办,传统的写法肯定是直接在上述代码的第14行后加入一条缓存用户信息的处理,但这样下去,以后每次增加这部分的需求是都无一例外的需要修改客户端的代码,并且实际上客户端发送请求的方法肯定不止一处,如果有一个新业务是需要在每个请求的方法中都添加呢。那又怎么办?现在上面的代码只是模拟,所以只需要加一行打印输出即可,真实的逻辑可比这复杂得多,到时候的代码量也是现在的好几倍,岂不麻烦死了?

为了解决这个问题,我们可以使用责任链模式。

修改版v1

增加一组类,使用责任链模式。如下。

// 责任链的顶级接口
// 定义责任链的核心功能
public interface Chain {
    // 指定下一个处理
    void setNext(Chain next);
    // 处理当前请求
    void handler(Map<String, String> request);
}
// 责任链的抽象父类
// 定义所有责任链对象共通的属性和方法
public abstract class AbstractChain implements Chain {
    // 持有下一个处理
    private Chain next;
    public Chain getNext() {
        return next;
    }
    @Override
    public void setNext(Chain next) {
        this.next = next;
    }
}
// 用户认证模块
public class AuthChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        String username = request.get("username");
        String password = request.get("password");
        if (!"admin".equals(username) || !"admin".equals(password)) {
            throw new RuntimeException("用户名或密码不正确!");
        }
        System.out.println("用户认证通过。");
        Chain next = this.getNext();
        if (next == null) {
            throw new RuntimeException("处理中断!");
        }
        next.handler(request);
    }
}
// Session缓存模块
public class SessionChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        System.out.println("缓存用户信息至Session。");
        Chain next = this.getNext();
        if (next == null) {
            throw new RuntimeException("处理中断!");
        }
        next.handler(request);
    }
}
// 业务处理模块
public class ProcessChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        System.out.println("正在处理请求的业务。");
    }
}

修改后,客户端代码的调用如下。

public class Client {
    public static void main(String[] args) {
        Map<String, String> request = new HashMap<>();
        request.put("username", "admin");
        request.put("password", "admin");
        service(request);
    }
    public static void service(Map<String, String> request) {
        Chain auth = new AuthChain();
        Chain session = new SessionChain();
        Chain process = new ProcessChain();
        auth.setNext(session);
        session.setNext(process);
        auth.handler(request);
    }
}

熟悉数据结构的同学肯定一眼就看出了责任链模式的本质:链表。将一个冗长的业务处理流程拆分成各个模块,使用时根据业务流程以链表的形式将其串联。不仅提升了各个模块代码的复用性,而且还能是的各个模块自由组合,极大的提升了开发效率。

总结

优点

  • 可以自由指定各个模块的执行顺序。
  • 各个模块遵循单一职责使得各个模块间解耦。
  • 新增业务流程时只需要增加新的模块即可,遵循开闭原则,提升了系统的可维护性。

缺点

  • 当业务流程过长时,组成的业务链也会非常的长,涉及到的对象过多可能会影响系统的性能。
  • 虽然各个模块可以自由组合,但是组合的工作实际上都放在了客户端,这无疑提升了客户端的代码复杂度。

适用场景

  • 业务流程类的功能都可以使用责任链模式。
    • 公司或政府部门各级对于文件的审批,需要各级各部门盖章签字,并且其中一环未通过就得打回重新审核。

标签:责任,Chain,void,request,next,---,new,设计模式,public
From: https://www.cnblogs.com/spoonb/p/16748754.html

相关文章

  • [Typescript] 92. Medium - PathParams
    typePathParams<Sextendsstring>=Sextends`/${string}/:${inferParam}/${inferREST}`?Param|PathParams<`/${REST}`>:Sextends`${string}/:${i......
  • 软件设计模式白话文系列(五)建造者模式
    1、描述将一个对象的构造过程进行封装,并按照一定顺序进行构造。通俗的讲,假如要创建电脑的对象,那么各个实例的属性不同,也就是电脑的配置各不相同.这个时候可以考虑用构......
  • gst-launch-1.0-录制视频
    文档手册gst-launch-1.0BUG:$'\r':commandnotfound当我们执行shell脚本的时候提示’\r’:commandnotfound,但是检查了很多次并没有发现什么问题。原因是wi......
  • CSP-S2022 总结
    调整了下心态开考顺序开题看完\(T1,T2\)直接开打\(T2\)的线段树,还是比较好写的然后思考先打\(T1\)呢还是拍\(T2\),最后决定拍\(T2\),稳一点发现随机数据很弱,决定......
  • Installing MongoDB-6.0.2 on openEuler-22.09
    一、InstallingMongoDB-6.0.2onopenEuler-22.091查看版本openEuler-22.09和CentOS8.3里安装无差别,直接一步到位好吧! X、OneStepSuccesssystemctlstopfi......
  • C++面经:C++多态-----虚函数、虚函数表、虚函数指针、虚继承
    1.虚函数引入类中之后,类会发生什么变化?首先我们创建一个空类A,然后创建一个类的对象a,并打印它的占用空间大小---为1   我们再往类中添加两个成员函数后,再返回对象......
  • ffmpeg-安装编译
    下载源码Linux:DownloadFFmpegWindows选择full版本CODEXFFMPEG-releasebuildsLinux安装方法一软件包工具安装:sudoaptinstallffmpeg,sudoyuminstallffmp......
  • [转载学习] Verilog-SystemVerilog中的force和release语法
    Verilog-SystemVerilog中的force和release语法本文属于转载,版权属于原博主—狮子座硅农(LeoICer)&星星在人间,博客原地址https://blog.csdn.net/qianniuwei321/article/de......
  • 每日一题-区间分组
    区间分组 sort(a.begin(),a.end()); priority_queue<int,vector<int>,greater<int>>q; for(inti=0;i<n;++i){ if(q.empty()orq.top()>=a[i].fir......
  • 每日一题-区间覆盖
    区间覆盖sort(a.begin(),a.end()); intans=0,las=s; for(inti=0;i<n;++i){ intj=i; intr=-2e9; while(j<nanda[j].first<=......