• NChain 0.1 项目——但愿是根救命稻草


    本文内容

    • NChain 概述
    • NChain 架构
    • 使用 NChain 演示

    最近做个项目,有流程控制。也就是,执行一个流程,依赖该流程前面的流程……

    比如,编写一个文档后,需要提交给二领导,二领导同意了,再由大领导审核,即 创建 –> 提交 –> 审核(提交或审核后,当然可以打回给创建或提交,也就是,二领导或大领导认为文档不妥)。但有时,看什么样文档,事不大的话,二领导全权处理就行了。或是,大领导很信任、肯定二领导的能力,交代一般事情你全权处理就好了,不用向我请示。

    再比如,政府部门的审批,他们的审批流程每年都在变,因为政策变了。

    现在,假设有流程 A –> B –> C –> D –> E –> F,从 A 可以任意跳流程(这仅仅是业务“跳”了,程序不会),也就是,A –> B,或 A –> C,或 A –> F,或 B –> D 等等。

    起初,不想搞得太复杂,但写着写着发现,目前的结构耦合得太紧,更要命是代码不易控制,调试也有困难——很困惑。想来想去,觉得改进成 NChain 的方式似乎可以解决我目前的困境。

    下面是 NChain 的结构。

    NChain 概述

    Chain.NET(又称 NChain),是 "Chain Of Resposibility" (CoR) 职责链模式在 .NET 和 Mono 平台上的一个实现。

    这个库的概念来自于 Java 平台的 Jakarta's Commons Chain

    Chain.NET 解决方案把标准的职责链模式(CoR design pattern )与命令模式(Command design pattern )相结合,以方便灵活地处理命令。

    Chain.NET 库提供了适当接口,用来扩展标准的 CoR。

    Chain.NET 可以做到如下几个方面:

    • 试想,若将对业务实体(可以是接口,可以是类,一般是抽象类/基类)的处理看成命令,抽象成处理单元。命令描述自己如何处理以及能处理哪些业务实体。
    • 对一个业务实体,可以执行多个命令。也就是,将形成一条命令链,将业务实体传递给这个链,这样业务实体就会经过链中所有命令的处理。
    • 当其中一个命令执行失败后,链会从当前一个命令的前一个命令开始回退,也就是撤销之前所有命令对业务实体的处理。

    这样,如上所示,流程 A –> B –> C –> D –> E –> F,以及业务 object,是经过 A –> B,还是 B –> D,构造你的链就行。这就是自动化流程。

    NChain 架构

    接口

    Chain.NET 库里有几个基本接口,可以实现标准的 CoR 模式。如下:

    • ICommand
    • IFilter
    • IChain
    • IContext

    ICommand 接口表示执行的(考虑要完成特定的执行状态)工作单元。

    IFilter 接口通过 postProcess 方法扩展标准的 ICommand,该方法总是由 IChain 执行,当 IFilterexecute 方法执行完后执行。

    IChain 接口表示 ICommand 有序集合,需要处理特定的 IContext。它扩展标准的 ICommand 接口。

    IContext 接口表示执行对 command 可用的上下文环境(状态信息)。在同一个 IChaincommand,可以重新抛出(把内部异常再抛出来)来互相通信,或返回执行结果。

    基类
    • ChainBase 类——IChain 接口的基类实现

    该类提供基本的“链”功能。通过把 context 传递给“链”中的 command,处理特定的 context。

    该类提供 addCommandremoveCommand 方法添加新的 command 到 “链”和从“链”中删除 command。

    • ContextBase 类——IContext 接口的基类实现

    该类继承 System.Collection.Hashtable 类。提供标准的 context 功能。

    • CommandBase

    抽象的 CommandBase 类提供两个常量,用来标识命令的执行结果。

    对于“链”的 command,可以不继承该类。command 可以返回定义在该类中的常量(true/false),以标识执行状态。

    图 1 NChain 结构

    图 1 NChain 结构

    使用 NChain 演示

    下面是 NChain 单元测试 Demo 给出的一个演示。

    • TestCommandBase 基类继承 NChainCommandBase 基类。TestCommandBase 基类是单元测试中所有“命令”类和“后续处理命令”类(如 SimpleAdderCommandSimpleFilterForwardCommand 等)的基类。
    • TestFilterBase 基类继承 TestCommandBase 基类。

    直观上理解,这个演示规定了两种命令:命令和后续命令。如执行一个“命令”,却失败了,此时需要执行“后续命令”。如下 ChainBaseexecute 方法的代码。

    “命令”的基类是 TestCommandBase,“后续命令”的基类为 TestFilterBase。“后续命令”也是“命令”,所以 TestFilterBase 要继承 TestCommandBase

    public bool execute(IContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context", "Context is null.");
        }
     
        isFrozen = true;
     
        bool savedResult = false;
     
        Exception savedException = null;
     
        int i = 0;
        int n = commandsList.Count;
        for (i = 0; i < n; i++)
        {
            try
            {
                savedResult = ((ICommand)commandsList[i]).execute(context);
     
                if (savedResult)
                {
                    break;
                }
            }
            catch (Exception e)
            {
                savedException = e;
                break;
            }
        }
     
        if (i == n)
        {
            i--;
        }
     
        bool isHandled = false;
     
        bool result = false;
     
        for (int j = i; j >= 0; j--)
        {
            if (commandsList[j] is IFilter)
            {
                try
                {
                    result = ((IFilter)commandsList[j]).postProcess(context, savedException);
     
                    if (result)
                    {
                        isHandled = true;
                    }
                }
                catch (Exception e)
                {
                    // ignore exception during postprocessing
                }
            }
        }
     
        // Return the exception or result state from the last execute()
        if ((savedException != null) && !isHandled)
        {
            throw savedException;
        }
        else
        {
            return (savedResult);
        }
    }

    备注:

    若一个“链”有 n 个命令,当执行到第 i 个命令失败后(调用 chain 里第 iICommandexectue 方法),就执行从 i0,将所有 ICommand 接口转换成 IFilter 接口,并执行其 postProcess 方法。

    相当于,执行一个命令失败后,撤销该命令。

    另外,上面代码还判断了 ICommand 是否属于 IFilter。若是,才进行转换,并执行 postProcess 方法。因为,不是所有的命令有其相应的撤销命令。比如数据库,DML 操作有事务,但 DDL 没有,也不需要。再比如操作系统,执行 delete 命令若失败,当然要撤销,可执行 dir 命令要是失败,就无所谓了。

    如图 2 所示,所有“命令”,包括“添加(SimpleAdderCommand 类)”、“删除(SimpleRemoverCommand 类)”、“向前(SimpleForwardCommand 类)”、“完成(SimpleCompletedCommand 类)”、“异常(SimpleExceptionCommand 类)”都继承 TestCommandBase 基类。每个类都有 execute 方法。

    图 2 命令类的类图

    图 2 命令类的类图

    如图 3 所示,所有“后续处理命令”,包括“向前(SimpleFilterForwardCommand 类)”、“完成(SimpleFilterCompleteCommand 类)”和“异常(SimpleFilterExceptionCommand 类)”也都继承 TestCommandBase 基类。当然是在继承它们应该继承的 IFilter 接口和 TestFilterBase 基类的基础上。

    图 3 后续处理类的类图

    图 3 后续处理命令类的类图

    下载 Demo 运行 nchain 里的 RuntimeInstantiation 例子需要 Spring.Net

    下载 命令模式

    下载 职责链模式

    下载 http://www.springsource.org/download/community?project=Spring.NET

  • 相关阅读:
    iOS 自动识别URL(链接)功能的实现
    iOS 如何查看崩溃日志
    Swift-- 闭包
    Swift--方法(函数)
    Swift--控制流
    Swift--字典的了解
    数据存储与访问之——SharedPreferences
    汇编指令之STOS、REP
    汇编指令之ADC、SBB、XCHG、MOVS指令
    pushad与popad
  • 原文地址:https://www.cnblogs.com/liuning8023/p/2520374.html
Copyright © 2020-2023  润新知