当前位置: 首页 > article >正文

设计模式-职责链模式Chain of Responsibility

职责链模式

  • 一、原理和实现
  • 二、实现方式
    • 1) 使用链表实现
    • 2) 使用数组实现
    • 3) 扩展

作用:复用和扩展,在实际的项目开发中比较常用。在框架开发中,我们也可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。

一、原理和实现

职责链模式的英文翻译是 Chain Of Responsibility Design Pattern。在 GoF 的《设计模式》中,它是这么定义的:

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.

翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止,实时上,在常见的使用场景中,我们的责任链并不是和概念中的完全一样。

  • 原始概念中,是直到链上的某个接收对象能够处理它为止。
  • 实际使用中,链上的所有对象都可以对请求进行特殊处理。

二、实现方式

1) 使用链表实现

第一种实现方式如下所示。其中,Handler 是所有处理器类的抽象父类,handle() 是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的 handle() 函数的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用 successor.handle())。HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

public abstract class Handler {
    // 拥有具体的处理方法,抽象
    protected Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    abstract void handle();
}


public class HandlerA extends Handler{
    @Override
    public void handle() {
        System.out.println("第一个过滤器");
        if (nextHandler != null) {
            nextHandler.handle();
        }
    }
}

public class HandlerB extends Handler{
    @Override
    public void handle() {
        System.out.println("第二个过滤器");
        if (nextHandler != null) {
            nextHandler.handle();
        }
    }
}

public class HandlerChain {
    private Handler head = null;
    private Handler tail = null;
    public void addHandler(Handler handler) {
        handler.setSuccessor(null);
        if (head == null) {
            head = handler;
            tail = handler;
            return;
        }
        tail.setSuccessor(handler);
        tail = handler;
    }
    public void handle() {
        if (head != null) {
            head.handle();
        }
    }
}

// 使用举例
public class Application {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

实际上,上面的代码实现不够优雅。处理器类的 handle() 函数,不仅包含自己的业务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在 handle() 函数中调用 successor.handle(),这就会导致代码出现 bug。

针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。重构之后的代码如下所示:

public abstract class Handler {
    protected Handler successor = null;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public final void handle() {
        boolean handled = doHandle();
        if (successor != null && !handled) {
            successor.handle();
        }
    }
    protected abstract boolean doHandle();
}

public class HandlerA extends Handler {
    @Override
    protected boolean doHandle() {
        boolean handled = false;
        //...
        return handled;
    }
}

public class HandlerB extends Handler {
    @Override
    protected boolean doHandle() {
        boolean handled = false;
        //...
        return handled;
    }
}
// HandlerChain和Application代码不变

2) 使用数组实现

我们再来看第二种实现方式,代码如下所示。这种实现方式更加简单。HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 handle() 函数中,依次调用每个处理器的 handle() 函数。

public interface IHandler {
    boolean handle();
}

public class HandlerA implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        //...
        return handled;
    }
}
public class HandlerB implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        //...
        return handled;
    }
}

public class HandlerChain {
    private List<IHandler> handlers = new ArrayList<>();
    public void addHandler(IHandler handler) {
        this.handlers.add(handler);
    }
    public void handle() {
        for (IHandler handler : handlers) {
            boolean handled = handler.handle();
            if (handled) {
                break;
            }
        }
    }
}
// 使用举例
public class Application {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

3) 扩展

在 GoF 给出的定义中,**如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。**这种变体也有两种实现方式:用链表存储处理器和用数组存储处理器,跟上面的两种实现方式类似,只需要稍微修改即可。

我这里只给出其中一种实现方式,如下所示。另外一种实现方式你对照着上面的实现自行修改。

public abstract class Handler {
    protected Handler successor = null;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public final void handle() {
        doHandle();
        if (successor != null) {
            successor.handle();
        }
    }
    protected abstract void doHandle();
}


public class HandlerA extends Handler {
    @Override
    protected void doHandle() {
        //...
    }
}

public class HandlerB extends Handler {
    @Override
    protected void doHandle() {
        //...
    }
}

public class HandlerChain {
    private Handler head = null;
    private Handler tail = null;
    public void addHandler(Handler handler) {
        handler.setSuccessor(null);
        if (head == null) {
            head = handler;
            tail = handler;
            return;
        }
        tail.setSuccessor(handler);
        tail = handler;
    }
    public void handle() {
        if (head != null) {
            head.handle();
        }
    }
}

// 使用举例
public class Application {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

http://www.kler.cn/news/234700.html

相关文章:

  • rediss集群 三主三从集群模式
  • nginx添加lua模块
  • Learn LaTeX 015 - LaTex Typeset 抄录
  • 2.11 运算符
  • Stable Diffusion 模型下载:Samaritan 3d Cartoon(撒玛利亚人 3d 卡通)
  • 一键打造属于自己漏扫系统
  • [缓存] - Redis
  • ChatGPT高效提问—prompt常见用法
  • Netty应用(六) 之 异步 Channel
  • Flink从入门到实践(三):数据实时采集 - Flink MySQL CDC
  • C#在窗体正中输出文字以及输出文字的画刷使用
  • 单片机学习笔记---蜂鸣器播放提示音音乐(天空之城)
  • 物联网和工业4.0
  • 算法-3-基本的数据结构
  • QT+OSG/osgEarth编译之八十四:osgdb_osg+Qt编译(一套代码、一套框架,跨平台编译,版本:OSG-3.6.5插件库osgdb_osg)
  • RabbitMQ——构建高性能消息传递的应用
  • 彩虹系统7.0免授权+精美WAP端模板源码
  • 基于微信小程序的校园故障维修管理系统的研究与实现
  • 探索NLP中的N-grams:理解,应用与优化
  • 【Web】Spring rce CVE-2022-22965漏洞复现学习笔记
  • 《CSS 简易速速上手小册》第8章:CSS 性能优化和可访问性(2024 最新版)
  • 格式化dingo返回内容
  • 算法竞赛进阶指南——基本算法(倍增)
  • NGINX upstream、stream、四/七层负载均衡以及案例示例
  • python从入门到精通(十八):python爬虫的练习案列集合
  • 【高阶数据结构】B-树详解
  • 如何入门AI Agent?
  • C++函数对象-运算符函数对象 - 逻辑运算 - 实现 !x 的函数对象 (std::logical_not)
  • Java 集合、迭代器
  • 跟着cherno手搓游戏引擎【24】开启2D引擎前的项目总结(包括前置知识汇总)