Loading...

设计模式学习总结:责任链模式

​ 本文为笔者在阅读一些书籍、博客、专栏等资料后所总结的个人对于责任链模式的笔记,由于笔者才疏学浅,若有不足之处,还望各位加以斧正,您的建议与鼓励都是笔者源源不断的前进动力。感谢!

文章大纲如下:

  • 责任链模式的简单认识
  • 责任链模式的简单应用
    • 两种责任链的实现方法:基于数组、链表
    • 责任链的一些简单应用
  • 责任链模式在源码中的体现
  • 责任链模式的优缺点

责任链模式简单认识

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ---- 《设计模式》GoF

中文意思为:

通过给多个对象处理请求的机会,避免请求的发送方与其接收方耦合。将接收对象串起来,并沿着链传递请求,直到有一个对象处理它。

笔者的将其理解为:

现在有一条责任链(其中包含着各种接收对象,也可以理解为处理器,其具有处理请求的方法)。发送方对责任链发送请求,责任链中的对象A接收到请求后,若其能处理该请求,则处理,否则就向下一个对象传递请求,直到有一个对象能对其进行处理。整个过程形成一条链。

此外,责任链模式还有另一种实现方式,即

请求进入责任链,对象A接收到请求后,若需要处理,则处理,处理后传递给下一个对象;若不需要处理,则直接传递给下一个对象。也就是说,其最终都会将该对象传递给链上的下一个对象,以此类推,直到最后一个对象处理后,方结束。此变体中的请求会被责任链中的每个处理器(对象)处理。

责任链模式的简单应用

这两种责任链的实现方法

以下实现代码参考自王争先生在极客时间上的《设计模式之美》所展示的代码片段。我在其中注释部分加入了自己的理解、总结。

第一种责任链(即处理后就返回)

此责任链的实现方法有两种,分别是数组实现和链表实现。

  • 数组实现

数组实现的大致模板:

/**
 * 处理者接口,定义了处理者的行为
 */
interface IHandler {
    boolean handle();
}

/**
 * 处理者A
 */
class HandlerA implements IHandler  {
    @Override
    public boolean handle() {
        boolean status = true;
        // 此处省略处理任务的方法……
        // 如果任务被处理了,就令status为true;否则令其为false
        System.out.println("[HandlerA]: status is " + status);
        return status;
    }
}

/**
 * 处理者B
 */
class HandlerB implements IHandler  {
    @Override
    public boolean handle() {
        boolean status = true;
        // 此处省略处理任务的方法……
        // 如果任务被处理了,就令status为true;否则令其为false
        System.out.println("[HandlerB]: status is " + status);
        return status;
    }
}

/**
 * 责任链
 */
class HandlerChain {
    // 责任链的数组存储对象
    private List<IHandler> handlerList = new ArrayList<>();
    
    // 向数组中添加handler的方法,添加成功返回true,反之为false
    public boolean addHandler(IHandler handler) {
        return this.handlerList.add(handler);
    }
    
    // 责任链调用处理者去处理请求
    public void handle() {
        // 遍历责任链上的处理者
        for (IHandler handler : handlerList) {
            boolean status = handler.handle();
            // 如果处理者返回的是true,就说明已经处理请求了,可以退出遍历了
            // 反之,继续遍历,直到被处理
            if (status == true) {
                break;
            }
        }
    }
}

/**
 * 基于数组实现的责任链(处理后就停止的责任链)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

输出结果为:

可见,HandleA处理了请求后,就不再往后传递请求了。

  • 链表实现

链表实现的大致模板:

/**
 * 处理者抽象类,定义了处理者的行为
 */
abstract class IHandler {
    // 下一个处理者
    protected IHandler nextHandler;

    // 子类需要实现的处理方法
    protected abstract boolean doHandle();

    // 设置下一个处理者
    public void setNextHandler(IHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 获取下一个处理者
    public IHandler getNextHandler() {
        return this.nextHandler;
    }

    // 父类调用子类的处理方法判断是否向后传递请求
    public final void handle() {
        boolean status = doHandle();
        if ((!status) && (this.nextHandler != null)) {
            this.nextHandler.handle();
        }
    }
}

/**
 * 处理者A
 */
class HandlerA extends IHandler  {
    @Override
    public boolean doHandle() {
        boolean status = false;
        // 此处省略处理任务的方法……
        // 如果任务被处理了,就令status为true;否则令其为false
        System.out.println("[HandlerA]: status is " + status);
        return status;
    }
}

/**
 * 处理者B
 */
class HandlerB extends IHandler  {
    @Override
    public boolean doHandle() {
        boolean status = true;
        // 此处省略处理任务的方法……
        // 如果任务被处理了,就令status为true;否则令其为false
        System.out.println("[HandlerB]: status is " + status);
        return status;
    }
}

/**
 * 责任链
 */
class HandlerChain {
    // 责任链的头结点
    private IHandler headHandler;
    // 责任链的尾结点
    private IHandler tailHandler;

    // 向责任链中添加处理者
    public void addHandler(IHandler handler) {
        // 把处理者的下一个结点设为空
        handler.setNextHandler(null);
        // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
        if (this.headHandler == null) {
            this.headHandler = handler;
            this.tailHandler = handler;
            return; // 设置好后就返回,因为后面要给head非空时的情况赋值
        }
        // headHandler非空时,把传进来的结点加入到链表尾部
        this.tailHandler.setNextHandler(handler); 
        this.tailHandler = handler;
    }

    // 责任链调用处理者去处理请求
    public void handle() {
        if (this.headHandler != null) {
            this.headHandler.handle();
        }
    }
}

/**
 * 基于链表实现的责任链(处理后就停止的责任链)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

输出结果:

第二种责任链(一直往后传递)

此责任链的实现方法也是两种,即数组、链表实现。

  • 数组实现

数组实现的大致模板:

/**
 * 处理者接口,定义了处理者的行为
 */
interface IHandler {
    // 实现类的处理方法
    void handle();
}

/**
 * 处理者A
 */
class HandlerA implements IHandler {
    @Override
    public void handle() {
        // 此处省略处理任务的方法…
        System.out.println("[HandlerA]: handle()");
    }
}

/**
 * 处理者B
 */
class HandlerB implements IHandler {
    @Override
    public void handle() {
        // 此处省略处理任务的方法……
        System.out.println("[HandlerB]: handle()");
    }
}

/**
 * 责任链
 */
class HandlerChain {
    // 责任链的存储对象
    List<IHandler> handlerList = new ArrayList<>();

    // 向责任链中添加处理者
    public HandlerChain addHandler(IHandler handler) {
        handlerList.add(handler);
        return this;
    }

    // 责任链调用处理者去处理请求
    public void handle() {
        for (IHandler handler : handlerList) {
            handler.handle();
        }
    }
}

/**
 * 基于数组实现的责任链(处理后就停止的责任链)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        IHandler handlerA = new HandlerA();
        IHandler handlerB = new HandlerB();
        handlerChain.addHandler(handlerA).addHandler(handlerB);
        handlerChain.handle();
    }
}

输出结果:

  • 链表实现

链表实现的大致模板:

/**
 * 处理者抽象类,定义了处理者的行为
 */
abstract class IHandler {
    // 下一个处理者
    protected IHandler nextHandler;

    // 子类需要实现的处理方法
    protected abstract void doHandle();

    // 设置下一个处理者
    public void setNextHandler(IHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 获取下一个处理者
    public IHandler getNextHandler() {
        return this.nextHandler;
    }

    // 父类调用子类的处理方法并向后请求
    public final void handle() {
        doHandle();
        // 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
        if (this.nextHandler != null) {
            System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
            this.nextHandler.handle();
        }
    }
}

/**
 * 处理者A
 */
class HandlerA extends IHandler  {
    @Override
    public void doHandle() {
        // 此处省略处理任务的方法…
        System.out.println("[HandlerA]: doHandler()");
    }
}

/**
 * 处理者B
 */
class HandlerB extends IHandler  {
    @Override
    public void doHandle() {
        // 此处省略处理任务的方法……
        System.out.println("[HandlerB]: doHandler()");
    }
}

/**
 * 责任链
 */
class HandlerChain {
    // 责任链的头结点
    private IHandler headHandler;
    // 责任链的尾结点
    private IHandler tailHandler;

    // 向责任链中添加处理者
    public void addHandler(IHandler handler) {
        // 把处理者的下一个结点设为空
        handler.setNextHandler(null);
        // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
        if (this.headHandler == null) {
            this.headHandler = handler;
            this.tailHandler = handler;
            return; // 设置好后就返回,因为后面要给head非空时的情况赋值
        }
        // headHandler非空时,把传进来的结点加入到链表尾部
        this.tailHandler.setNextHandler(handler);
        this.tailHandler = handler;
    }

    // 责任链调用处理者去处理请求
    public void handle() {
        if (this.headHandler != null) {
            this.headHandler.handle();
        }
    }
}

/**
 * 基于链表实现的责任链(处理后就停止的责任链)
 */
public class Demo {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());
        handlerChain.handle();
    }
}

输出结果:

可以看到,HandlerA在处理了请求后,又传递给了HandlerB,HandlerB也对请求进行了处理。

责任链的一些应用

一个小案例

假设有一场景如下:我们需要对用户的登录请求进行登陆前的数据校验、登录表单的数据校验、权限数据的校验。

在这样的场景中,我们通常会这样去实现(模拟实现,省略了部分操作、代码):

public class Demo {
    public boolean login(User user) {
        String userName = user.getUserName();
        String password = user.getPassword();
        // 非空校验(数据校验部分)
        if (StringUtils.isEmpty(userName) && StringUtils.isEmpty(password)) {
            System.out.println("数据校验不通过!");
            return false;
        }
        System.out.println("数据校验通过!");

        // 用户名、密码校验,模拟验证
        if (userName != "xingzhi" && password != "987") {
            System.out.println("用户名或密码错误!");
            return false;
        }
        System.out.println("用户名、密码校验通过!");
        user.setRoleName("visitor");

        // 用户角色校验
        if (user.getRoleName() != "admin") {
            System.out.println("无权限登录!");
            return false;
        }
        System.out.println("权限验证通过");
        return true;
    }

    // 模拟登录接口调用
    public static void main(String[] args) {
        Demo demo = new Demo();
        User user = new User().setUserName("xingzhi").setPassword("987");
        if (demo.login(user)) {
            System.out.println("登陆成功!");
        } else {
            System.out.println("登陆失败!");
        }
    }
}

// 模拟一个用户类
class User {
    private String userName;
    private String password;
    private String roleName;

    public String getUserName() {
        return userName;
    }

    public User setUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public String getPassword() {
        return password;
    }

    public User setPassword(String password) {
        this.password = password;
        return this;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

输出结果如下:

这样确实把需求实现了,但是

根据这一场景,责任链模式是一个很好的选择。因为进行完一个校验后还需要后续的校验,所以我们选择使用第二种责任链的链表实现方式来完成需求(需要注意的是,当某一处理者不通过时,就不往下传递了,所以我在Ihandler接口类中新增了handlerStatus属性用来记录处理者的处理状态)。代码如下:(但我感觉我这个案例可能不太好.....)

/** 处理者父类 */
abstract class abstractHandler {
    // 下一个处理者
    protected abstractHandler nextHandler;
    protected boolean handlerStatus;

    // 子类需要实现的处理方法
    protected abstract boolean doHandle(User user);

    // 设置下一个处理者
    public void setNextHandler(abstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 获取下一个处理者
    public abstractHandler getNextHandler() {
        return this.nextHandler;
    }

    // 父类调用子类的处理方法并向后请求
    public final boolean handle(User user) {
        handlerStatus = doHandle(user);
        if (!handlerStatus) return false;
        // 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
        if (this.nextHandler != null) {
            System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
            this.nextHandler.handle(user);
        }
        return true;
    }
}

/**
 * 数据校验处理者
 */
class ValidateHandler extends abstractHandler {
    @Override
    public boolean doHandle(User user) {
        System.out.println("[ValidateHandler]: doHandler()");
        // 如果用户名和密码不为空,则进行处理
        if (!StringUtils.isEmpty(user.getUserName()) && !StringUtils.isEmpty(user.getPassword())) {
            System.out.println("数据校验成功!");
            return true;
        } else {
            System.out.println("数据格式不通过!");
            return false;
        }
    }
}

/**
 * 身份校验处理者
 */
class AuthenticationHandler extends abstractHandler {
    @Override
    public boolean doHandle(User user) {
        System.out.println("[AuthenticationHandler]: doHandler()");
        if ("xingzhi".equals(user.getUserName()) && "987".equals(user.getPassword())) {
            user.setRoleName("admin");
            System.out.println("身份验证通过!");
            return true;
        } else {
            System.out.println("身份验证不通过!");
            return false;
        }
    }
}

/** 角色校验处理者 */
class RoleHandler extends abstractHandler {
    @Override
    public boolean doHandle(User user) {
        System.out.println("[RoleHandler]: doHandler()");
        if ("admin".equals(user.getRoleName())) {
            System.out.println("橘色校验通过!");
            return true;
        } else {
            System.out.println("角色校验不通过!");
            return false;
        }
    }
}

/**
 * 责任链
 */
class LoginHandlerChain {
    // 责任链的头结点
    private abstractHandler headHandler;
    // 责任链的尾结点
    private abstractHandler tailHandler;

    // 向责任链中添加处理者
    public LoginHandlerChain addHandler(abstractHandler handler) {
        // 把处理者的下一个结点设为空
        handler.setNextHandler(null);
        // 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
        if (this.headHandler == null) {
            this.headHandler = handler;
            this.tailHandler = handler;
            return this; // 设置好后就返回,因为后面要给head非空时的情况赋值
        }
        // headHandler非空时,把传进来的结点加入到链表尾部
        this.tailHandler.setNextHandler(handler);
        this.tailHandler = handler;
        return this;
    }

    // 责任链调用处理者去处理请求
    public boolean handle(User user) {
        if (this.headHandler != null) {
            return this.headHandler.handle(user);
        }
        return false;
    }
}

// 模拟一个用户类
class User {
    private String userName;
    private String password;
    private String roleName;
    private String Permission;

    public String getUserName() {
        return userName;
    }

    public User setUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public String getPassword() {
        return password;
    }

    public User setPassword(String password) {
        this.password = password;
        return this;
    }

    public String getRoleName() {
        return roleName;
    }

    public User setRoleName(String roleName) {
        this.roleName = roleName;
        return this;
    }

    public String getPermission() {
        return Permission;
    }

    public User setPermission(String permission) {
        Permission = permission;
        return this;
    }
}

/**
 * 基于链表实现的责任链(处理后就停止的责任链)
 */
public class Demo {
    public static void main(String[] args) {
        User user = new User()
                .setUserName("xingzhi")
                .setPassword("987");
        LoginHandlerChain loginHandlerChain = new LoginHandlerChain()
                .addHandler(new ValidateHandler())
                .addHandler(new AuthenticationHandler())
                .addHandler(new RoleHandler());
        if (loginHandlerChain.handle(user)) {
            System.out.println("登陆成功!欢迎您:" + user.getUserName());
        } else {
            System.out.println("登陆失败!");
        };
    }
}

运行结果如图:

在这个场景中使用责任链模式就可以满足开闭原则,提高代码的扩展性。

比如,现在需要在角色认证后进行权限认证,这时我们只需要增加一个权限验证类就行了,但是我这个User类还得改改....所以我这个案例举得不太好(有点又臭又长了)....仅供参考...哈哈...

/** 权限校验处理者 */
class PermissionHandler extends abstractHandler {
    @Override
    public boolean doHandle(User user) {
        System.out.println("[PermissionHandler]: doHandler()");
        if ("menu:add".equals(user.getPermission())) {
            System.out.println("权限校验通过!");
            return true;
        } else {
            System.out.println("权限校验不通过!");
            return false;
        }
    }
}

责任链模式在源码中的体现

在看过了我的糟糕的案例后,我们来看看大师的作品,源码中的责任链模式:

/**
 * 先留个坑...有时间了来填...
 */

责任链模式的优缺点

最后,总结下责任链模式的优缺点。

优点

  • 将请求的发送者和接收者解耦。
  • 简化对象,减少代码的复杂性。(处理者不需要知道责任链的内部构造以及如何处理的)
  • 满足开闭原则,提高代码的扩展性。(动态地新增或删除责任)

缺点

  • 不容易观察运行时的特征,出现问题不好排查。(如果某一节点出现问题,则容易造成系统崩溃)
  • 并不一定保证请求一定会执行。(责任链过长时,请求没被处理或者处理时间过长,会影响整体性能)
posted @ 2020-12-14 20:34  _轻舟  阅读(518)  评论(0编辑  收藏  举报