apache-commons

chain的使用

chain是apache提供的通用方法中的责任链,通过chain可以很优雅的将代码中大量的if else替换;并且可以简化代码,业务实现中,可以更专注的编写业务代码,不需要过多的进行逻辑判断。

背景

最近在开发的项目中,不同的处理类型,需要调用不同的第三方接口;部分类型需要调用十几个第三方接口,并且调用第三方的接口是需要按照一定的顺序进行调用的。
如果在代码中通过if else进行判断,会有很多if else的语句,对于后面的维护以及代码的阅读造成一定的困难。
面对此问题,考虑使用责任链进行解决。

工作原理介绍

责任链中的角色

责任链的角色
其中,顶级的角色有三个:Command、Chain、Context
Command和Chain的关系是组合模式(Composite patterrn):Chain由一或多哥Command组成,他自己本身也是一个Command,此设计,让责任链的配置文件可以更灵活(当然,灵活的同时,也会造成维护成本的增加)
Context仅仅是一个存放了名称-值对的集合。接口Context在这里作为一个标记接口:它扩展了java.util.Map但是没有添加任何特殊的行为。于此相反,类ContextBase不仅提供了对Map的实现而且增加了一个特性:属性-域透明。这个特性可以通过使用Map的put和get 方法操作JavaBean的域,当然这些域必须使用标准的getFoo和setFoo方法定义。那些通过JavaBean的“setter”方法设置的值,可以通过对应的域名称,用Map的get方法得到。同样,那些用Map的put方法设置的值可以通过JavaBean的“getter”方法得到。
例如,我们可以创建一个专门的context提供显式的customerName属性支持。

详细使用教程

加载依赖

        <dependency>
            <groupId>commons-chain</groupId>
            <artifactId>commons-chain</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-digester/commons-digester -->
        <dependency>
            <groupId>commons-digester</groupId>
            <artifactId>commons-digester</artifactId>
            <version>1.8</version>
        </dependency>

配置责任链(因为本人觉得xml的配置看起来更加清晰明了,所以,使用xml的配置方式)

<catalog name="auto-sales">
    <chain name="toc-sale">
        <command id="CompanyTradingCommond" className="com.yatsenglobal.cloud.chain.commond.CompanyTradingCommand"/>
    </chain>
    <chain name="tob-sale">
        <command id="CompanyTradingCommond" className="com.yatsenglobal.cloud.chain.commond.CompanyTradingCommand"/>
    </chain>
</catalog>

设置目录加载程序,加载xml配置文件

@Component
@Slf4j
public class CatalogLoader {

    private static final String CONFIG_FILE = "/commond/sale-chain.xml";

    private ConfigParser parser;

    private Catalog catalog;

    @PostConstruct
    public void init() {
        log.info("开始加载上传销售数据的责任链配置");
        parser = new ConfigParser();
        try {
            parser.parse(this.getClass().getResource(CONFIG_FILE));
        } catch (Exception e) {
            throw new BusinessException("无法加载目录配置文件!");
        }
    }

    public CatalogLoader() {
    }

    public Catalog getCatalog() {
        catalog = CatalogFactoryBase.getInstance().getCatalog("auto-sales");
        return catalog;
    }

    public Command getCommand(String chain) {
        return getCatalog().getCommand(chain);
    }

    public static void main(String[] args) throws Exception {
        CatalogLoader catalogLoader = new CatalogLoader();
        Catalog catalog = catalogLoader.getCatalog();
        Command command = catalog.getCommand("toc-sale");
        SaleContext context = new SaleContext();
        command.execute(context);
    }
}

扩展责任链的上下文信息(用于责任链中各个链条的信息传递)

@Data
public class SaleContext extends ContextBase {

    @ApiModelProperty("上传状态")
    private String uploadStatus;

    @ApiModelProperty("批次号")
    private String code;

    @ApiModelProperty("汇总上传列表")
    List<KdSaleUploadSummary> sumList;

}

使用模板方法的设计模式进行扩展责任链的节点(使该节点与项目实际使用更加适配)

@Slf4j
public abstract class BaseCommand implements Command {

    @Override
    public boolean execute(Context context) throws Exception {
        if (context instanceof SaleContext) {
            SaleContext saleContext = (SaleContext) context;

            if (!needExe(saleContext.getUploadStatus())) {
                log.debug("此次履行,不需要执行");
                return false;

            }

            try {
                doExecute(saleContext);
            } catch (Exception e) {
                throw new SaleUploadException(getUploadStatus(),e.getMessage());
            }
            return false;
        } else {
            log.warn("context中的对象类型不是SaleContext,不处理");
            return true;
        }
    }

    /**   业务逻辑具体实现   */
    protected abstract void doExecute(SaleContext saleContext) throws Exception;

    /**   需要执行上传KD的操作   */
    protected abstract boolean needExe(String uploadStatus);

    /**   执行失败的上传状态   */
    protected abstract String getUploadStatus();

}

实现具体的责任链节点

@Slf4j
public class CompanyTradingCommand extends BaseCommand {

    @Override
    protected void doExecute(SaleContext saleContext) throws Exception {
        log.info("进入公司间交易,批次号={}",saleContext.getCode());
    }

    @Override
    protected boolean needExe(String uploadStatus) {
        if (KdDeliveryContant.SaleUploadStatus.NOT_BEGIN.equals(uploadStatus)) {
            return true;
        }
        return false;
    }

    @Override
    protected String getUploadStatus() {
        return KdDeliveryContant.SaleUploadStatus.COMPANY_TRADING_FAIL;
    }

}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {

    @Resource
    private CatalogLoader catalogLoader;

    @Test
    public void testCreatePurchaseOrder() throws Exception {
        Command command = catalogLoader.getCommand("toc-sale");
        SaleContext context = new SaleContext();
        context.setCode("testCode");
        context.setUploadStatus("1");
        command.execute(context);
    }
}

参考文献

链接: 入门.
链接:深入使用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值