程序内的程序?

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

或修改后的实施:

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

Arn*_*hez 19

像这样的嵌套过程在这个XML相关代码中有意义.为了处理所有的节点,一个递归调用ProcessNode需要.您必须注意,有时,内部函数需要访问比一些参数更多的数据.

潜在的实施可能是:

  • 使用"平面"程序,如在您的实施中;
  • 使用"嵌套"过程,如在原始实现中;
  • 创建一个专用class(或record+方法)将保持私有implementation所述单元的一部分.

当然,第三种选择听起来更易于维护.它将允许明确分离过程,允许使用其方法本地的变量.使用record(或object旧版本的Delphi)将允许在主过程的堆栈上分配处理对象,因此您不需要编写Obj := TInterType.Create; try .. finally Obj.Free.但是如果您使用object请注意某些新版本的Delphi 存在编译问题 - 您最好使用record方法.

"平坦"的过程风格是恕我直言并不比"嵌套"过程更好,更糟糕的是,因为它需要在内部调用中添加额外的参数,或者使用一些全局变量.顺便说一句,每次调用都有很多变量会增加堆栈空间并降低速度.

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

Delphi IDE和内部调试器可以很好地处理嵌套过程.恕我直言,它可以用于一些小代码(也就是说,可以在同一屏幕高度上读取的东西).然后,当您需要更多进程时,专用record/object方法和显式变量将更易于维护.但"平坦"选项是恕我直言,不能编码.

我刚写了这些实施模式的博客文章,这会呈现出的一些源代码快速排序的实施,将使用尽可能少的堆栈空间成为可能,并避免对过程中的嵌套过程的调用,并使用私人专用object.

在所有情况下,不要害怕创建一些内部对象/类来实现您的算法.Delphi的最新版本甚至可以在class定义中使用私有类型- 但有时候,我觉得让内部对象完全隐私到implementation单元的一部分感觉更舒服,即甚至不显示为interface单元部分的私有成员.

类不仅用于在单元外发布您的进程:OOP也适用于实现模式.您的代码将更易于维护,并且在大多数情况下,该self参数将用于同时引用所有相关数据,因此您的代码也可能更快更轻!


Chu*_*ars 11

用这样的内部程序编码是一种风格问题.有人可能会认为它是"更干净"...... 就像在"面向对象编程"中听到的那样,将所有相关数据和例程封装在一个东西中的同样意义......但它也有缺点:编码更难最初是正确的,更难以测试,更难以让许多程序员理解(因此可能更难维护).

定义一个内部过程可以防止未来的程序员意外调用内部过程并期望它做一些合理的事情.内部程序甚至没有定义 - 因此无法在外部/全局级别调用.

定义内部过程还意味着在外部/全局命名空间中发生名称冲突的可能性较小,因为内部例程对该命名空间没有任何贡献.(这是一个很好的例子:有多少不同的东西都被命名为"ProcessNode(...)"?)

如上所述,在大多数语言中,内部例程具有"特殊"访问权限,否则将是不可见的本地数据类型和变量.