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

Mah*_*hya 5 architecture delphi workflow software-design

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

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

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

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

  1. [文档控制器]从承包商处接收文档
  2. [文档控制器]使用核对表检查文档
  3. [文档控制器]将文档发送到[项目经理]
  4. [项目经理]在文档中应用和操作
  5. [项目经理]将文档发送到[文档控制器]
  6. [文档控制器]存档文档.

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

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

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

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

谢谢,
Mahya

Sam*_*Sam 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;
Run Code Online (Sandbox Code Playgroud)

这样,当数据库中的运行时参数发生更改时,应用程序的行为将有所不同,而无需重新编译。