2

我有一棵树。

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 方法:

这里有一个演示测试

实施在这里

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

4

5 回答 5

3

您可以编写一个带有状态的“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()

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

于 2009-02-18T23:32:58.147 回答
2

嵌套构造在这里可能是一个不错的选择。不要暴露孩子名单是个好主意。

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>();
}
于 2009-02-18T23:43:43.020 回答
2
  • 理想情况下,您需要一种将语言扩展到自定义类型文字的方法。C# 没有这个,所以你必须找到另一种方法。

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

  • 以功能构造XElement为例

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

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

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

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

于 2009-02-19T00:16:58.227 回答
1

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

<Node description="desc">
    Parent
    <Node description="desc1">
        Child 1
        <Node description="desc1">
            Grandchild 1
        </Node>
    </Node>
    <Node description="desc2">
        Child 2
    </Node>
</Node>
于 2009-02-18T23:36:04.067 回答
1

我会将实现拆分为 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);
    }
}

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

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

于 2009-02-19T00:39:46.923 回答