由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

设计模式—责任链模式

JAVA 西门飞冰 624℃
[隐藏]

1.定义

责任链模式Chain是⼀种⾏为设计模式, 允许你将请求沿着处理者链进⾏发送。 收到请求后, 每个处理者均可对请求进⾏处理, 或将其传递给链上的下个处理者。

2.案例分析

假如你正在开发⼀个在线订购系统。 你希望对系统访问进⾏限制, 只 允许认证⽤户创建订单。 此外, 拥有管理权限的⽤户也拥有所有订单 的完全访问权限。

简单规划后, 你会意识到这些检查必须依次进⾏。 只要接收到包含⽤户凭据的请求, 应⽤程序就可尝试对进⼊系统的⽤户进⾏认证。 但如果由于⽤户凭据不正确⽽导致认证失败, 那就没有必要进⾏后续检查了。

image-20220921224544996

在接下来的⼏个⽉⾥, 你实现了后续的⼏个检查步骤。

⼀位同事认为直接将原始数据传递给订购系统存在安全隐患。 因此你新增了额外的验证步骤来清理请求中的数据。

过了⼀段时间, 有⼈注意到系统⽆法抵御暴⼒密码破解⽅式的攻击。为了防范这种情况, 你⽴刻添加了⼀个检查步骤来过滤来⾃同⼀ IP 地址的重复错误请求。

⼜有⼈提议你可以对包含同样数据的重复请求返回缓存中的结果, 从⽽提⾼系统响应速度。 因此, 你新增了⼀个检查步骤, 确保只有没有满⾜条件的缓存结果时请求才能通过并被发送给系统。

image-20220921224630594

检查代码本来就已经混乱不堪, ⽽每次新增功能都会使其更加臃肿。修改某个检查步骤有时会影响其他的检查步骤。 最糟糕的是, 当你希望复⽤这些检查步骤来保护其他系统组件时, 你只能复制部分代码,因为这些组件只需部分⽽⾮全部的检查步骤。

系统会变得让⼈⾮常费解, ⽽且其维护成本也会激增。 你在艰难地和这些代码共处⼀段时间后, 有⼀天终于决定对整个系统进⾏重构。

3.解决方案

与许多其他⾏为设计模式⼀样, 责任链会将特定⾏为转换为被称作处理者的独⽴对象。 在上述示例中, 每个检查步骤都可被抽取为仅有单个⽅法的类, 并执⾏检查操作。 请求及其数据则会被作为参数传递给该⽅法。

模式建议你将这些处理者连成⼀条链。 链上的每个处理者都有⼀个成员变量来保存对于下⼀处理者的引⽤。 除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动, 直⾄所有处理者都有机会对其进⾏处理。

最重要的是: 处理者可以决定不再沿着链传递请求, 这可⾼效地取消所有后续处理步骤。

在我们的订购系统示例中, 处理者会在进⾏请求处理⼯作后决定是否继续沿着链传递请求。 如果请求中包含正确的数据, 所有处理者都将执⾏⾃⼰的主要⾏为, ⽆论该⾏为是身份验证还是数据缓存。

image-20220921225040079

4.责任链模式的主要角色

1、处理者(Handler)声明了所有具体处理者的通用接口。

2、基础处理者(Base Handler)是一个可选的类,你可以将所有处理者共用的样本放置在其中。

3、具体处理者(Concrete Handler)包含处理请求的实际代码。每个处理者接收到实际请求后,都必须决定是否进行处理,以及是否沿着链传递请求。

4、客户端(Client)可根据程序逻辑一次性或者动态地生成链。值得注意的是,请求可发送给链上的任意一个处理者,而非必须是第一个处理者。

5.代码示例

顶层接口:

public interface Handler {
    // 定义下一个处理器是什么,从外侧进行传入
    public void setNext(Handler next);
    // 定义当前的处理器要做什么事情
    public void handle(Map<String,Object> request);
}

抽象处理器:

public abstract class AbstractHandler implements Handler {
    // 内部持有下一个Handler对象
    private Handler next;

    // 从外部传入Handler 进行链表的构建形成,完成上级和下级Handler之间的关联
    @Override
    public void setNext(Handler next) {
        this.next = next;
    }

    // 获取下一个执行的Handler
    public Handler getNext() {
        return next;
    }
}

认证处理器:

public class AuthHandler extends AbstractHandler{
    @Override
    public void handle(Map<String,Object> request) {
        // 执行业务逻辑认证操作
        if(request.get("username").equals("admin") && request.get("password").equals("admin"))
        {
            System.out.println("⽤户信息认证通过");
            // 用户认证通过调用下一个处理器的handle方法进行执行
            if(this.getNext() != null){
                this.getNext().handle(request);
            }else{
                throw new RuntimeException("请求没有被送达ProceedHandler");
            }
            // 用户认证未通过,中断责任链执行
        }else{
            throw new RuntimeException("⽤户名或密码错误");
        }
    }
}

缓存处理器:

public class CacheHandler extends AbstractHandler{
    @Override
    public void handle(Map<String,Object> request) {
        System.out.println("已将当前请求转存⾄Redis缓存");
        // 如果当前处理后还有其他处理器,则执行后续处理器
        if(this.getNext() != null){
            this.getNext().handle(request);
        }else{
            throw new RuntimeException("请求没有被送达ProceedHandler");
        }
    }
}

指标收集处理器:

public class MetricsHandler extends AbstractHandler{
    @Override
    public void handle(Map<String,Object> request) {
        System.out.println("已获取当前运⾏指标并发送⾄Prometheus");
        // 如果当前处理后还有其他处理器,则执行后续处理器
        if(this.getNext() != null){
            this.getNext().handle(request);
        }else{
            throw new RuntimeException("请求没有被送达ProceedHandler");
        }
    }
}

业务转发处理器,责任链处理的最终节点

public class ProceedHandler extends AbstractHandler{
    @Override
    public void handle(Map<String,Object> request) {
        System.out.println("请求已被转发给业务系统,进行后续业务处理");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        // 实例化调用链上的处理器
        Handler authHandler = new AuthHandler();
        Handler cacheHandler = new CacheHandler();
        Handler metricsHandler = new MetricsHandler();
        Handler proceedHandler = new ProceedHandler();

        // 进行责任链的构建
        authHandler.setNext(cacheHandler);
        cacheHandler.setNext(metricsHandler);
        metricsHandler.setNext(proceedHandler);

        // 模拟请求数据
        Map request = new HashMap<>();
        request.put("username", "admin");
        request.put("password", "admin");

        // 进行责任链的执行
        authHandler.handle(request);
    }
}

6.责任链模式适合应⽤场景

1、当程序需要使⽤不同⽅式处理不同种类请求, ⽽且请求类型和顺序预先未知时, 可以使⽤责任链模式。该模式能将多个处理者连接成⼀条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进⾏处理。 这样所有处理者都有机会来处理请求。

2、当必须按顺序执⾏多个处理者时, 可以使⽤该模式。⽆论你以何种顺序将处理者连接成⼀条链, 所有请求都会严格按照顺序通过链上的处理者。

3、如果所需处理者及其顺序必须在运⾏时进⾏改变, 可以使⽤责任链模式。如果在处理者类中有对引⽤成员变量的设定⽅法, 你将能动态地插⼊和移除处理者, 或者改变其顺序。

7.责任链模式优点

你可以控制请求处理的顺序。

单⼀职责原则。 你可对发起操作和执⾏操作的类进⾏解耦。

开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

8.责任链模式缺点

不能保证每个请求⼀定被处理。由于⼀个请求没有明确的接收者,所以不能保证它⼀定会被处理,该请求可能⼀直传到链的末端都得不到处理。

对⽐较⻓的职责链,请求的处理可能涉及多个处理对象,系统性能将受到⼀定影响。

职责链建⽴的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置⽽导致系统出错,如可能会造成循环调⽤。

转载请注明:西门飞冰的博客 » 设计模式—责任链模式

喜欢 (1)or分享 (0)