delphi - 程序内的程序?

标签 delphi

我在网上找到了这段代码,其中有一个过程内有一个过程。 我不明白为什么作者会选择这样写。 我注意到的是正在执行的递归函数。

为什么他不像我见过的大多数代码那样将程序分开。

他的实现:

procedure XML2Form(tree : TJvPageListTreeView; XMLDoc : TXMLDocument);
var
  iNode : IXMLNode;

  procedure ProcessNode(
    Node : IXMLNode; 
    tn   : TTreeNode);
  var
    cNode : IXMLNode;
  begin
    if Node = nil then Exit;
    with Node do
    begin
      tn := tree.Items.AddChild(tn, Attributes['text']);
      tn.ImageIndex := Integer(Attributes['imageIndex']);
      tn.StateIndex := Integer(Attributes['stateIndex']);
    end;

    cNode := Node.ChildNodes.First;
    while cNode <> nil do
    begin
      ProcessNode(cNode, tn);
      cNode := cNode.NextSibling;
    end;
  end; (*ProcessNode*) 
begin
  tree.Items.Clear;
  XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML');
  XMLDoc.Active := True;

  iNode := XMLDoc.DocumentElement.ChildNodes.First;

  while iNode <> nil do
  begin
    ProcessNode(iNode,nil);
    iNode := iNode.NextSibling;
  end;
  XMLDoc.Active := False;
end; (* XML2Form *)


procedure Form2XML(tree: TJVPageListTreeView);
var
  tn : TTreeNode;
  XMLDoc : TXMLDocument;
  iNode : IXMLNode;

  procedure ProcessTreeItem(
    tn    : TTreeNode;
    iNode : IXMLNode);
  var
    cNode : IXMLNode;
  begin
    if (tn = nil) then Exit;
    cNode := iNode.AddChild('item');
    cNode.Attributes['text'] := tn.Text;
    cNode.Attributes['imageIndex'] := tn.ImageIndex;
    cNode.Attributes['stateIndex'] := tn.StateIndex;
    cNode.Attributes['selectedIndex'] := tn.SelectedIndex;

    //child nodes
    tn := tn.getFirstChild;
    while tn <> nil do
    begin
      ProcessTreeItem(tn, cNode);
      tn := tn.getNextSibling;
    end;
  end; (*ProcessTreeItem*)
begin
  XMLDoc := TXMLDocument.Create(nil);
  XMLDoc.Active := True;
  iNode := XMLDoc.AddChild('tree2xml');
  iNode.Attributes['app'] := ParamStr(0);

  tn := tree.TopItem;
  while tn <> nil do
  begin
    ProcessTreeItem (tn, iNode);

    tn := tn.getNextSibling;
  end;

  XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML'));
  XMLDoc := nil;
end; (* Form2XML *)

或修改后的实现:

procedure ProcessNode(Node : IXMLNode; tn : TTreeNode);
var
  cNode : IXMLNode;
begin
  if Node = nil then Exit;
  with Node do
  begin
    tn := tree.Items.AddChild(tn, Attributes['text']);
    tn.ImageIndex := Integer(Attributes['imageIndex']);
    tn.StateIndex := Integer(Attributes['stateIndex']);
  end;

  cNode := Node.ChildNodes.First;
  while cNode <> nil do
  begin
    ProcessNode(cNode, tn);
    cNode := cNode.NextSibling;
  end;
end; (*ProcessNode*)

procedure ProcessTreeItem(tn : TTreeNode; iNode : IXMLNode);
var
  cNode : IXMLNode;
begin
  if (tn = nil) then Exit;
  cNode := iNode.AddChild('item');
  cNode.Attributes['text'] := tn.Text;
  cNode.Attributes['imageIndex'] := tn.ImageIndex;
  cNode.Attributes['stateIndex'] := tn.StateIndex;
  cNode.Attributes['selectedIndex'] := tn.SelectedIndex;

  //child nodes
  tn := tn.getFirstChild;
  while tn <> nil do
  begin
    ProcessTreeItem(tn, cNode);
    tn := tn.getNextSibling;
  end;
end; (*ProcessTreeItem*)

procedure XML2Form(tree : TJvPageListTreeView; XMLDoc : TXMLDocument);
var
  iNode : IXMLNode;
begin
  tree.Items.Clear;
  XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML');
  XMLDoc.Active := True;

  iNode := XMLDoc.DocumentElement.ChildNodes.First;

  while iNode <> nil do
  begin
    ProcessNode(iNode,nil);
    iNode := iNode.NextSibling;
  end;
  XMLDoc.Active := False;
end;

procedure Form2XML(tree: TJVPageListTreeView);
var
  tn : TTreeNode;
  XMLDoc : TXMLDocument;
  iNode : IXMLNode;
begin
  XMLDoc := TXMLDocument.Create(nil);
  XMLDoc.Active := True;
  iNode := XMLDoc.AddChild('tree2xml');
  iNode.Attributes['app'] := ParamStr(0);

  tn := tree.TopItem;
  while tn <> nil do
  begin
    ProcessTreeItem (tn, iNode);

    tn := tn.getNextSibling;
  end;

  XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML'));
  XMLDoc := nil;
end; (* Form2XML *)

最佳答案

像这样的嵌套过程在这个 XML 相关代码中确实有意义。要处理所有节点,需要对 ProcessNode 进行递归调用。您必须注意,有时,内部函数需要访问的数据远多于几个参数。

潜在的实现可能是:

  • 在您的实现中使用“扁平”程序;
  • 使用“嵌套”过程,如原始实现中一样;
  • 创建一个专用的(或记录+方法),该类对于实现部分保持私有(private)单位。

当然,第三个选项听起来更易于维护。它将允许过程的清晰分离,并且允许使用其方法的本地变量。使用记录(或旧版本Delphi的对象)将允许在主过程的堆栈上分配处理对象,因此您不需要写Obj := TInterType.Create;尝试..最后Obj.Free。但如果您使用对象,请注意一些新版本的Delphi has compilation issue - 你应该更好地将record与方法一起使用。

恕我直言,“平面”过程风格并不比“嵌套”过程更好,甚至更糟,因为它需要向内部调用添加额外的参数,或者使用一些全局变量。顺便说一下,每次调用都有很多变量会增加堆栈空间,并降低速度。

“嵌套”风格实际上是面向OOP的。当调用内部函数时,编译器将寄存器中的调用者堆栈基址传递给嵌套函数(就像对象的附加 self 参数一样)。因此内部函数能够访问所有调用者堆栈变量,就像它们在私有(private)对象中声明一样(第三种解决方案)。

Delphi IDE 和内部调试器可以很好地处理嵌套过程。恕我直言,这对于一些小代码段(即可以在相同屏幕高度上读取的代码)来说可能是有意义的。然后,当您需要更多流程时,带有方法和显式变量的专用记录/对象将更易于维护。但恕我直言,“扁平”选项不需要编码。

我刚刚 written a blog article about these implementation patterns ,它将呈现 QuickSort 实现的一些源代码,它将使用尽可能少的堆栈空间,并将避免调用过程内的嵌套过程,并使用专用的私有(private) 相反,对象

在所有情况下,不要害怕创建一些内部对象/类来实现您的算法。 Delphi 的最新版本甚至允许在 class 定义中使用私有(private)类型 - 但有时,我觉得让内部对象完全私有(private)于单元的 implementation 部分会更舒服,即甚至不作为单元的接口(interface)部分的私有(private)成员出现。

类不仅用于在单元外部发布流程:OOP 还适用于实现模式。您的代码将更易于维护,并且在大多数情况下,self 参数将用于一次性引用所有关联数据,因此您的代码也可能更快、更轻!

关于delphi - 程序内的程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10670840/

相关文章:

delphi - 表单关闭时 Acrobat Reader ActiveX 访问冲突

delphi - IEnumerable.GetEnumerator() 在 Delphi 6 中返回 IEnumVariant

delphi - 如何将 SMTP Amazon SES 与 Delphi 结合使用?

delphi - 如何从 TDictionary 获取 key ?

delphi - 如何重现此代码完成错误?

Delphi:Ms Jet 4.0仍然是开发桌面数据库应用程序的不错选择吗?

delphi - 如何修复 'Unable to find record. No key specified' ?

delphi - 如何在Delphi 10.2中使用ToolsAPI获取当前项目的版本号

delphi - IDE 中缺少 `Project Page Options`,该怎么办?

delphi - 我想通过 delphi 代理使用 smtp 发送电子邮件