使用Delphi实现win32应用程序的动态流程图

时间:2011-06-06 09:12:13

标签: delphi architecture workflow software-design

Delphi XE,Win32应用程序,SQL Server 2005数据库。

我正在实施质量管理体系。 我有一些预定义的Process Maps将它们应用到我的应用程序/系统中。我被要求拥有所有的事务(我不确定它是否是正确的词)动态,所以每当他们修改流程图时它会影响应用程序(当然没有重新编译或补丁)

这是一个更清楚解释的例子:

假设一个文档控制模块,我们有一个流程图:

  1. [凭证控制器]从承包商
  2. 接收凭证
  3. [文档控制器]使用清单检查文档
  4. [文档控制器]将文档发送到[项目经理]
  5. [项目经理]在文件中适用和采取行动
  6. [项目经理]将文档发送到[文档控制器]
  7. [文档控制器]存档文档。
  8. 现在,应用程序应该从数据库中读取其功能的参数。 假设收到并检查了文档(1和2),现在发送它。一旦按下“保存”按钮,系统应检查谁应该是该文件的接收者并将文件发送给他/她。 在我们的例子中,接收者是[项目经理]。但是,稍后他们可能会决定将流程图更改为   - “3- [文档控制器]将文档发送给[Project Architect]”。 因此,系统应按流程图中的定义进行操作。

    我想知道实现这样的系统(Delphi XE,win32)的正确方法是什么?

    我有一些想法,但不确定它是否合适: 对于流程图中的每个流程,我可以使用一种唯一ID定义服务,然后从数据库中读取服务并在应用程序层中调用它(具有相关参数)。在这种情况下,我不确定每个服务是否应该是一个dll或包文件,我相信拥有这么多库文件是错误的,因为服务不会很少!

    我希望我能很好地解释我的问题,如果时间太长,我很抱歉。 如果不清楚,请告诉我。

    谢谢,
      Mahya

1 个答案:

答案 0 :(得分:2)

在我看来,您希望为应用程序执行的每个“业务功能”应用一些通用规则,从而实现某种形式的质量管理。我将使用“业务功能”来表示可能跨越源代码中的多个技术功能和过程的逻辑操作。

没有明确的“正确方法”,但有些想法比其他想法更好。

使用数据库存储动态数据显然是个好主意。例如,您可以将每个业务功能建模为单独的实体(每个业务功能具有一个数据库记录)。无论您需要管理每个业务功能的处理所需的变量,都将确定必要的字段。你肯定需要一个唯一的id。

没有理由将业务功能编码到单独的dll中,但是我将它们放在源代码中的单独单元中,或者根据它们的操作类型或对您的业务有意义的其他逻辑分组对功能进行分组。 。您需要更改调用业务功能的方式。你需要间接调用它们,换句话说,你可以调用一个通用的PerformFunction例程,也许在任何适合你的数据结构中传递一个函数标识符和一些其他参数。随着参数在数据库中的更改(根据您的示例,将文档发送给谁,项目经理或项目架构师?),只要您实现了所有可能的排列,就会相应地修改业务功能的操作。给定每个业务功能的变量数量可能会出现。我确定文档收件人的电子邮件地址不是你想到的全部。这里有一些可能有用的代码。我不是说这是好的代码,只是传达了这个想法。

type
  TFunctionRuntimeParameter = record
    FunctionID: integer; // this better be unique
    FunctionName: string;  // something intelligible for display purposes
    ReportToEmail: string; // who to send the end report document
    AuditLevel: integer;  // for example vary the degree of auditing/logging
    variableABC: TDateTime;  // could be a date that influences something
  end;

function TMyForm.GetRuntimeParametersFromDB(aFunctionID: integer): TFunctionRuntimeParameters;
var
  tempResult: TFunctionRuntimeParameters;
begin
  // For brevity I'm going to assume an existing query object connected to your db.
  qry.SQL.Add('Select * From BusinessFunctions Where Function_ID = :FunctionID');
  qry.ParamByName('FunctionID').AsInteger := aFunctionID;
  qry.Open;
  tempResult.FunctionID := qry.FieldByName('Function_ID').AsInteger; // don't ask me Y!
  tempResult.FunctionName := qry.FieldByName('Function_Name').AsString;
  tempResult.ReportToEmail := qry.FieldByName('Report_To_Email').AsString;
  tempResult.AuditLevel := qry.FieldByName('Audit_Level').AsInteger;
  tempResult.variableABC := qry.FieldByName('ABC').AsDateTime;
  result := tempResult;
  qry.Close;
end;

procedure TMyForm.PerformFunction(aFunctionID: integer; FRP: TFunctionRuntimeParameters);
var
  MyReportDocument: TMyReportDocument;
begin
  if (FRP.AuditLevel > 0) then
    // do something special before starting the function

  case aFunctionID of
    101: MyReportDocument := DoBusinessFunctionABC;
    102: MyReportDocument := DoBusinessFunctionDEF;
    103: MyReportDocument := DoBusinessFunctionXYZ;
  end;

  SendReportDocumentByEmailTo(MyReportDocument, FRP.ReportToEmail);

  if ((Now - FRP.variableABC) > 28) then
    // perhaps do something special every 4 weeks!

  if (FRP.AuditLevel > 0) then
    // do something special after the function has finished
end;

procedure TMyForm.btnBusinessFunctionXYZClick(Sender: TObject);
var
  FunctionID: integer;
  FunctionRuntimeParameters: TFunctionRuntimeParameters; // record that mimics db entry
begin
  FunctionID := 1234; // or you might prefer an enum
  FunctionRuntimeParameters := GetFunctionRuntimeParametersFromDB(FunctionID);
  PerformFunction(FunctionID, FunctionRuntimeParameters);
end;

这样,随着数据库中运行时参数的更改,应用程序将以不同的方式运行,而无需重新编译。