Apache commons chain 初探

Stella981
• 阅读 872

Apache commons chain 是什么

Apache common chain 是对责任链设计模式的改造封装,让使用者更加方便的使用。

简单回顾一下责任链设计模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述责任链(Chain of Responsibility)模式的:

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任

关键点

  • 链是一系列节点的集合
  • 链的各个节点可随意拆分和组装

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系, 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。

责任链适用的场景

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

  • 你想在不明确指定接受者的情况下,想过个对象中的一个提交一个请求。

  • 可处理一个请求的对象集合应该被动态指定。

简单例子

abstract class Handler {

    private Handler nextHandler;

    public Handler getNextHandler() {
        return nextHandler;
    }

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

    public abstract void doHandler();

}

class ConcreteHandler extends Handler {

    @Override
    public void doHandler() {

        if (getNextHandler() != null) {

            System.out.println("还有责任链");
            getNextHandler().doHandler();
        } else {

            System.out.println("我自己处理" + toString());
        }

    }
}

设计模式主体架构

角色

抽象处理者角色(Handler):定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。 具体处理者角色(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。 抽象处理者角色

public abstract class Handler {  
      
    /** 
     * 持有后继的责任对象 
     */  
    protected Handler successor;  
    /** 
     * 示意处理请求的方法,虽然这个示意方法是没有传入参数的 
     * 但实际是可以传入参数的,根据具体需要来选择是否传递参数 
     */  
    public abstract void handleRequest();  
    /** 
     * 取值方法 
     */  
    public Handler getSuccessor() {  
        return successor;  
    }  
    /** 
     * 赋值方法,设置后继的责任对象 
     */  
    public void setSuccessor(Handler successor) {  
        this.successor = successor;  
    }  
      
}  

具体处理者角色

public class ConcreteHandler extends Handler {  
    /** 
     * 处理方法,调用此方法处理请求 
     */  
    @Override  
    public void handleRequest() {  
        /** 
         * 判断是否有后继的责任对象 
         * 如果有,就转发请求给后继的责任对象 
         * 如果没有,则处理请求 
         */  
        if(getSuccessor() != null)  
        {              
            System.out.println("放过请求");  
            getSuccessor().handleRequest();              
        }else  
        {              
            System.out.println("处理请求");  
        }  
    }  
  
} 

客户端类

public class Client {  
  
    public static void main(String[] args) {  
        //组装责任链  
        Handler handler1 = new ConcreteHandler();  
        Handler handler2 = new ConcreteHandler();  
        handler1.setSuccessor(handler2);  
        //提交请求  
        handler1.handleRequest();  
    }  
  
}  

Apache CommonsChain

CommonsChain实现了Chain of Responsebility和Command模式,其中的Catalog + 配置文件的方式使得调用方和Command的实现方的耦合度大大的降低,提高了灵活性。

如何使用

简单写了一个实现:

/**
 * <p>Test implementation of <code>Chain</code> that exposes the
 * <code>getCommands()</code> method publicy.</p>
 */

public class TestChain extends ChainBase {


    /**
     * 获取责任链中所有加入的命令
     * @return 责任链中的命令
     */
    public Command[] getCommands() {

    return (commands);

    }
 public static void main(String[] args) throws Exception {
        TestChain testChain=new TestChain();
        Context context=new ContextBase();
        /**
         * 加入执行命令1
         */
        testChain.addCommand(new Command() {
            public boolean execute(Context context) throws Exception {
                System.out.println("执行命令1");
                return false;
            }
        });
        /**
         * 加入执行命令2
         * 注意:返回值为true 表示执行到此为止
         */
        testChain.addCommand(new Command() {
            public boolean execute(Context context) throws Exception {
                System.out.println("执行命令2");
                return true;
            }
        });
        /**
         * 加入执行命令3
         */
        testChain.addCommand(new Command() {
            public boolean execute(Context context) throws Exception {
                System.out.println("执行命令3");
                return false;
            }
        });
        //执行链中的命令
        testChain.execute(context);
        //打印链中的所有命令
        Command[] commands=testChain.getCommands();
        for(Command command:commands){
            System.out.println(command.getClass());
        }
    }

}

** 执行结果 **

- 执行命令1
- 执行命令2
- class org.apache.commons.chain.config.TestChain$1
- class org.apache.commons.chain.config.TestChain$2
- class org.apache.commons.chain.config.TestChain$3

基本对象

  • 1. Command接口。它是Commons Chain中最重要的接口,表示在Chain中的具体某一步要执行的命令。它只有一个方法:boolean execute(Context context)。如果返回true,那么表示Chain的处理结束,Chain中的其他命令不会被调用;返回false,则Chain会继续调用下一个Command,直到:

  • [x] > Command返回true;

  • [x] > Command抛出异常;

  • [x] > Chain的末尾;

  • 2. Context接口。它表示命令执行的上下文,在命令间实现共享信息的传递。Context接口的父接口是Map,ContextBase实现了Context。对于web环境,可以使用WebContext类及其子类(FacesWebContext、PortletWebContext和ServletWebContext)。

  • 3. Chain接口。它表示“命令链”,要在其中执行的命令,需要先添加到Chain中。Chain的父接口是Command,ChainBase实现了它。

使用配置文件

test-config.xml
<catalog>
  <chain name="Execute">
    <command id="1"
        className="org.apache.commons.chain.impl.DelegatingCommand"/>
    <command id="2"
        className="org.apache.commons.chain.impl.DelegatingCommand"/>
    <command  id="3"
        className="org.apache.commons.chain.impl.ExceptionCommand"/>
  </chain>
</catalog>

装入配置文件

public class ConfigParserTestCase extends TestCase {


    private static final String DEFAULT_XML =
        "/org/apache/commons/chain/config/test-config.xml";
     // ------------------------------------------------------------ Constructors
    /**
     * Construct a new instance of this test case.
     *
     * @param name Name of the test case
     */
    public ConfigParserTestCase(String name) {
        super(name);
    }
    // ------------------------------------------------------ Instance Variables
    /**
     * <p>The <code>Catalog</code> to contain our configured commands.</p>
     */
    protected Catalog catalog = null;
    /**
     * <p>The <code>Context</code> to use for execution tests.</p>
     */
    protected Context context = null;
    /**
     * <p>The <code>ConfigParser</code> instance under test.</p>
     */
    protected ConfigParser parser = null;
    // ---------------------------------------------------- Overall Test Methods
    /**
     * Set up instance variables required by this test case.
     */
    public void setUp() {
        catalog = new CatalogBase();
        context = new ContextBase();
        parser = new ConfigParser();
    }
    /**
     * Return the tests included in this test suite.
     */
    public static Test suite() {
        return (new TestSuite(ConfigParserTestCase.class));
    }
    /**
     * Tear down instance variables required by this test case.
     */
    public void tearDown() {
        parser = null;
        context = null;
        catalog = null;
    }
    /**
       执行测试方法
    **/
    public void testExecute2c() throws Exception {

        load(DEFAULT_XML);
        try {
            catalog.getCommand("Execute").execute(context);
        } catch (ArithmeticException e) {
            assertEquals("Correct exception id",
                         "3", e.getMessage());
        }
        checkExecuteLog("1/2/3");
    }
    /** 
       从配置文件中加载配置信息
    **/
    protected void load(String path) throws Exception {
        parser.parse(this.getClass().getResource(path));
        catalog = CatalogFactoryBase.getInstance().getCatalog();
    }


}

注意:使用配置文件的话,需要使用Commons Digester。而Digester则依赖:Commons Collections、Commons Logging和Commons BeanUtils。

  • 4. Filter接口。它的父接口是Command,它是一种特殊的Command。除了Command的execute,它还包括一个方法:boolean postprocess(Context context, Exception exception)。Commons Chain会在执行了Filter的execute方法之后,执行postprocess(不论Chain以何种方式结束)。Filter的执行execute的顺序与Filter出现在Chain中出现的位置一致,但是执行postprocess顺序与之相反。如:如果连续定义了filter1和filter2,那么execute的执行顺序是:filter1 -> filter2;而postprocess的执行顺序是:filter2 -> filter1。

  • 5. Catalog接口。它是逻辑命名的Chain和Command集合。通过使用它,Command的调用者不需要了解具体实现Command的类名,只需要通过名字就可以获取所需要的Command实例。

  • 6.的使用。配置文件的引入,使得Commons Chain的灵活性大大的提高。在实际的使用过程中,存在着同一个Command被多个Chain使用的情形。如果每次都书写Command的类名,尤其是前面的包名特别长的情况下,是非常枯燥的。而的作用就是为了解决这样的麻烦。通过定义Command和Chain的别名,来简化书写。配置文件,可以书写成:

       <chain name="CommandChain">
              <command1 id="1"/>
              <filter1 id="2"/>
              <lookupCommand name="chain_command3" optional="true"/>
              <command2 id="3"/>
       </chain>
      
       <chain name="chain_command3">
              <command3 id="3"/>
       </chain>
      
       <command1 name="command4"/>
    

参考:

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Dubbo架构设计与源码解析(三)责任链模式
责任链模式是设计模式中简单且常见的设计模式,可能我们日常中也会经常应用责任链模式,dubbo中的责任链模式将灵活性发挥的很充分。
京东云开发者 京东云开发者
11个月前
一分钟学会、三分钟上手、五分钟应用,快速上手责任链框架详解 | 京东云技术团队
责任链模式是开发过程中常用的一种设计模式,在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式,通常需要自己来实现,但自己临时实现的责任链既不通用,也很容易产生框架与业务代码耦合不清的问题,增加CodeReview的成本。
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
Java设计模式之责任链模式
引入责任链模式责任链模式顾名思义,责任链模式(ChainofResponsibilityPattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会
Wesley13 Wesley13
2年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这