65.9K
CodeProject 正在变化。 阅读更多。
Home

构建向导描述的逻辑层

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (21投票s)

2005年8月8日

CPOL

8分钟阅读

viewsIcon

41902

downloadIcon

120

本文描述了用于描述向导的逻辑层的构建和使用方法。

图 1 测试应用程序的面板。

引言

在编写一个用于分步编辑和维护复杂文档的程序时,我产生了一个想法,能够以简单而有效的方式解决这个问题。

过了一段时间,我意识到我解决特定问题的方式代表了一种更广泛问题的通用方法,因此可能对其他人有用。这就是我决定为您的网站写一篇短文来分享这个想法的原因。考虑到关于编程艺术的论文不多,这尤其合适。

我将要处理的一类问题称为向导。从某种意义上说,任何安装过程都是向导的最简单示例。用户需要经历一系列对话框,选择相关选项并运行安装程序。示意性地,这一系列事件可以描绘成下图:

向导允许用户跳回一个或多个步骤,以便在需要时重新执行它们。

本质上,还有许多其他过程可以称为向导,并由类似于图 2 的方案表示。举几个例子,我们提到以下过程:

  1. 填写表单和执行数据管理中的复杂事务,
  2. 测试和设置众多设备,
  3. 校准测量设备。

这个列表并不完整,读者可以轻松地添加自己的示例来继续。

更一般地说,一个向导类型的问题可以表述如下:存在一组任务;它们按特定顺序执行,该顺序由特定应用程序规定的某些逻辑考虑决定。

重要的是,向导不同于常规的流程图。流程图的引擎会验证任务是否可以执行;仅当任务可以执行时,它才会自动运行任务。例如,如果一个任务是某个函数,那么一旦提供了函数参数,该函数就会被执行。

向导有一个独特的特点:任务不是自动执行的;它们由应用程序用户手动运行,而向导的引擎只是在每个时间实例检测哪些任务可供运行,并允许用户运行它们。但是,最终决定执行哪些任务以及何时执行的是用户。

让我们考虑图 3 所示的过程。它由 5 个任务组成。请注意,任务 1 到任务 3 不依赖于其他任务,因此可以随时以任何顺序执行。任务 4 只能在任务 1 和任务 2 都完成后才能执行。任务 5 在任务 3 和任务 4 完成后即可运行。

如果此时要运行任务 2,那么任务 4 和任务 5 也可以重新执行。需要注意一个重要说明:如果一个任务被重新执行,并且存在其他任务的结果依赖于该任务,那么这些其他任务也必须被重新执行。这种问题也属于向导类型,尽管它更复杂一些。在每一步,用户都关心两个问题:

  1. 哪些任务可以(或应该)完成?
  2. 还有哪些任务尚未完成?

程序员的问题更为复杂:他/她应该知道如何实现这种逻辑?通常,一切都以传统方式完成。在每一步,都会分析当前进程的状态并做出决策。例如:

if (IsDone(Task1) && IsDone(Task2) && 
          IsDone(Task3) && IsDone(Task4) && !IsDone(Task5))
{
    …
     Done(Task5);
    …
}

如果任务数量庞大,并且它们之间的链接很复杂,那么提出的解决方案将变得令人绝望地麻烦。此外,逻辑上的一点小改动都会导致需要对代码进行重大修改。调试本身就是一个问题……

有没有办法让我们的生活更轻松?我认为有一种方法可以节省时间。我将在下一节进行解释。我只想说,我的解决方案绝不是唯一的解决方案。

计算模型

将任务表示为类似于图 3 的图表是构建计算模型的起点。任何任务都可以表示为一个功能块,见图 4。

该框代表一个任务,具有两个主要属性:

  1. 输入条件列表。
  2. 输出条件列表。

输入条件由布尔变量向量给出。如果所有变量都设置为 true,则可以执行任务。

显然,这会限制某些功能:每个任务都附加了某个操作(事务、计算、输入/输出活动等)。但是,此时,我们更关心任务是否被执行,而不是其具体含义。

一旦任务完成,输出变量向量将被设置为 true

应该提到的是,输入和输出条件设定了任务之间的链接。上述模型允许构建一个自动求解器,该求解器能够在流程的每个阶段回答前面提出的两个问题:

  1. 哪些任务可以(或应该)完成?
  2. 还有哪些任务尚未完成?

模型实现

该系统是用 C# 实现的。选择这种语言的唯一原因是这是我工作场所当前的环境。也可以使用 Java、C/C++、DELPHI 等。解决方案附在本文章中。下面我将解释实现中的主要部分:

1. 任务

Task 对应于任务的概念。此类实现了接口 ITask。此接口最重要的元素是:

  • 方法 Done;它模仿执行任务的过程。

  • 属性 IsDone;它允许检查任务是否已完成。

Task 的构造函数的输入参数是:任务的标识符,输入(radarIsReadyoperatorIsReady)和输出(detectionIsDone)条件列表(Condition 对象将在第 2 页描述)。

ITask objectDetection
    =    new Task("ObjectDetection",
            new Conditions(detectionIsDone),
            new Conditions(radarIsReady,operatorIsReady))
;

一组 Task 类型的对象构成了系统。该系统通过具有 IEnvironment 接口的对象实现。

函数 Done 执行以下操作:

  1. 它将输出条件设置为 true;
  2. 它将属性 IsDone 设置为 true;
  3. 它根据输出的变化修改系统(Reset);

2. 条件

如前所述,条件是一个普通的布尔变量。此处,变量由接口 ICondition 描述,并在类 Condition 中实现。用户只需使用构造函数创建此对象即可。

ICondition condition = new Condition();

3. 环境

EnvironmentTask 类型对象的集合。Environment 对象的主要元素是:

  • 方法 Ready,它提供了一个可运行任务的列表。
  • 方法 Reset,它刷新系统状态。用户通过 Task 对象的操作 Done(第 1 页)间接调用此方法。

4. 主要操作

用户应执行三个主要操作:

  1. 描述系统。
  2. 运行任务。
  3. 检查系统状态。

最后两个步骤可以无限重复。

5. 系统描述

图 3 中所示的系统由以下过程描述:

ICondition
    postConditionTask1    = new Condition();
ICondition
    postConditionTask2    = new Condition();
ICondition
    postConditionTask3    = new Condition();
ICondition
    postConditionTask4    = new Condition()
;

ITask
    Task1    = new Task("Task1",
                new Conditions(postConditionTask1),new Conditions());
ITask
    Task2    = new Task("Task2",
                new Conditions(postConditionTask2),new Conditions());
ITask
    Task3    = new Task("Task3",
                new Conditions(postConditionTask3),new Conditions());
ITask
    Task4    = new Task("Task4",
                new Conditions(postConditionTask4),
                new Conditions(postConditionTask1,postConditionTask2));
ITask
    Task5    = new Task("Task5", 
                new Conditions(),
                new Conditions (postConditionTask3,postConditionTask4));
IEnvironment
    environment    =    new Environment(
                        new Tasks (Task1,Task2,Task3,Task4,Task5))
;

6) 运行任务

要运行任务,必须为特定的 Task 调用 Done 方法,例如:

Task1.Done(environment);

7) 系统状态验证

我们使用方法 IEnvironment::Ready

特别是,在创建系统并运行 Ready 过程后,可以检查任务 1 和任务 2(参见第 5 页)是否已准备好运行。

通过属性 IsDone,可以检查是否没有任务被完成。

ITasks    list    = environment.Ready()
;
Console.WriteLine("Task1 is ready to execution : {0}",list.Contains(Task1));
Console.WriteLine("Task2 is ready to execution : {0}",list.Contains(Task2))
;
Console.WriteLine("Task1 is done : {0}",Task1.IsDone);
Console.WriteLine("Task2 is done : {0}",Task2.IsDone);
Console.WriteLine("Task3 is done : {0}",Task3.IsDone);
Console.WriteLine("Task4 is done : {0}",Task4.IsDone);
Console.WriteLine("Task5 is done : {0}",Task5.IsDone)
;

限制

如图 5 所示,禁止直接和间接循环。

注意:上述应用程序假设方案中不包含直接或间接循环。

测试应用程序

测试应用程序描述了图 6 所示的过程。

为了说明起见,每个按钮都对应一个特定的任务。任务的执行通过点击按钮来模拟。相邻的复选框指示任务的状态:已完成/未完成,参见图 1。

项目简述

随本文提供的解决方案包含 4 个项目:

  • 项目 Environment 包含所有接口以及实现它们的类。这只是解决方案的核心。
  • 项目 TestConsole 说明了创建项目方案和运行一些向导命令的最简单方法。
  • 项目 LogicLayer 描述了图 6 中所示的图。
  • 项目 TestWinApplication 是一个 GUI 应用程序。它说明了向导如何仅使用最少的图形工具来运行,参见图 1。

历史

该应用程序大约在一年前编写了几天。我认为它可能会引起广大读者的兴趣,因此决定提交这篇短文。

© . All rights reserved.