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

原文地址:https://www.cnblogs.com/liuning8023/p/2520374.html