【问题标题】:Elegant and maintainable way of populating Tree structures in c#在 c# 中填充树结构的优雅且可维护的方式
【发布时间】:2012-06-08 05:24:06
【问题描述】:

我有一棵树。

class TreeNode {
    public TreeNode(string name, string description) {
        Name = name;
        Description = description;
    }
    string Name { get; set; }
    string Description { get; set; }
    public List<TreeNode> Children = new List<TreeNode>();
}

我想填充一个大的用于单元测试目的。我真的很想保持干燥。

出于说明目的,我的树具有以下结构

父级,说明 孩子 1,描述 1 孙子 1, desc1 孩子 2,描述 2

您将如何以一种优雅且可维护的方式填充树?

我发现这段代码非常重复且容易出错:

var parent = new TreeNode("Parent", "desc");
var child1 = new TreeNode("Child 1", "desc1");
var child2 = new TreeNode("Child 2", "desc2");
var grandchild1 = new TreeNode("Grandchild 1", "desc1");

parent.Children.Add(child1);
parent.Children.Add(child2);

child1.Children.Add(grandchild1);

编辑

我最终采用了 DSL 方法:

一个演示test lives here

implementation is here

它使用一个构建器和一个简单的 DSL。

【问题讨论】:

    标签: c# design-guidelines


    【解决方案1】:

    您可以编写一个带有状态的“TreeBuilder”来节省一些连接混乱:

    TreeBuilder builder = new TreeBuilder();
    
    builder.AddNode("Parent", "desc"); // Adds a node, and sets the cursor to it
    builder.AddLeaf("Child 1", "desc1"); // Adds a node and leaves the cursor at the Parent
    builder.AddNode("Child 2", "desc2");
    builder.AddLeaf("Grandchild 1", "desc1");
    builder.Up(); // Moves the cursor to the parent
    builder.AddNode("Child 3", "desc3");
    
    root = builder.GetRoot()
    

    另一种方法是用一些简单的格式发明一个简单的配置文件/字符串。

    【讨论】:

      【解决方案2】:
      • 理想情况下,您需要一种将语言扩展到自定义类型的文字的方法。 C# 没有这个,所以你必须找到另一种方法。

      • 您可以制作内部 DSL,通常具有流畅的界面

      • 关注XElement example of functional construction

      • 使用自定义解析器创建一个外部 DSL。如果你仔细设计语言,解析器会很容易。

      • 使用 XML。基本上这是一种创建外部 DSL 并免费获取解析器的方法。

      外部 DSL 选项很好,因为当您阅读它们时,您会知道只有数据,并且不必担心代码结构的含义。另外,数据就是文件,文件就是数据。这使得通过更改文件交换数据变得容易,并且更容易准备好文件更改历史。最后,当非程序员提供数据时,外部 DSL 也很好。

      这里的权衡是时间与价值。 您将拥有多少数据/多久会更改/谁会更改数据是您必须回答的问题。

      【讨论】:

        【解决方案3】:

        嵌套构造在这里可能是一个不错的选择。最好不要公开孩子的名单。

        class Program
        {
            static void Main(string[] args)
            {
                var parent = 
                    new TreeNode( "Parent", "desc", new TreeNode[] { 
                        new TreeNode( "Child 1", "desc1", new TreeNode[] { 
                            new TreeNode( "Grandchild 1", "desc1" ) } ),
                        new TreeNode( "Child 2", "desc2" ) } );
            }
        }
        
        class TreeNode
        {
            public TreeNode(string name, string description, IEnumerable<TreeNode> children)
                : this(name, description)
            {
                _children.AddRange(children);
            }
        
            public TreeNode(string name, string description)
            {
                Name = name;
                Description = description;
            }
        
            public string Name { get; set; }
            public string Description { get; set; }
        
            public IEnumerable<TreeNode> Children
            {
                get
                {
                    return _children.AsReadOnly();
                }
        
                set
                {
                    _children.Clear();
                    _children.AddRange(value);
                }
            }
        
            private List<TreeNode> _children = new List<TreeNode>();
        }
        

        【讨论】:

          【解决方案4】:

          您可以使用填充树的简单解析器编写树内容的简单 XML 表示。以下将给出您在上面指定的结构。

          <Node description="desc">
              Parent
              <Node description="desc1">
                  Child 1
                  <Node description="desc1">
                      Grandchild 1
                  </Node>
              </Node>
              <Node description="desc2">
                  Child 2
              </Node>
          </Node>
          

          【讨论】:

          • +1。这是 XML 实际上唯一有用的东西之一,因为它基本上是一种人类可读的树形形式。
          • 与嵌入在字符串中的 DSL 相比,使用 XML 的最大优势在于围绕它的大量工具。
          【解决方案5】:

          我会将实现拆分为 TreeClass 和 TreeNodeClass

          树类有成员变量

          TreeNodeClass root
          

          用方法

          TreeNodeClass addAtRoot(data) 
          

          返回他们刚刚创建的节点

          TreeNodeClass 还需要一个 AddChild() 方法,它也会返回它刚刚添加的节点。

          然后你可以做类似的事情

          addAtRoot(rootData).AddChild(childData).AddChild(grandchildData);
          

          使用类似的东西来随机生成一棵树

          AddRecursively(TreeNodeClass root)
          {
              numChildren = SomeRandomNumber;
              While(numChildren > 0)
              {
                 CTreeNodeClass newnode = root.AddChild(SomeRandomData);
                 AddRecursively(newnode);
              }
          }
          

          主要思想是您要返回刚刚添加到树中的节点。

          您可能还想让孩子知道它的父母,因为这有时会很方便。

          【讨论】:

            猜你喜欢
            • 2019-06-27
            • 1970-01-01
            • 1970-01-01
            • 2015-04-25
            • 2023-04-03
            • 2011-10-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多