在c#中填充树结构的优雅且可维护的方式
我有一棵树。
class TreeNode { public TreeNode(string name, string description) { Name = name; Description = description; } string Name { get; set; } string Description { get; set; } public List Children = new List(); }
我想填充一个大的unit testing用途。 我真的想把东西放干。
为了说明的目的,我的树具有以下结构
家长,降序 孩子1,desc1 孙子1,desc1 孩子2,desc2
您将如何以优雅可维护的方式填充树木?
我觉得这段代码非常重复且容易出错:
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。
-
理想情况下,您需要一种方法将语言扩展为自定义类型的文字 。 C#没有这个,所以你必须找到另一种方法。
-
您可以制作内部DSL ,通常具有流畅的界面 。
-
遵循function构造的
XElement
示例 。 -
使用自定义解析器创建外部DSL 。 如果您仔细设计语言,解析器可以很容易。
-
使用XML 。 基本上这是一种创建外部DSL并免费获得解析器的方法。
外部DSL选项很好,因为当你阅读它们时,你知道只有数据 ,而且不必担心理解代码结构。 此外,数据是文件,文件是数据。 这样可以通过更改文件轻松交换数据,并且更容易准备好文件更改历史记录。 最后,当非程序员提供数据时,外部DSL是好的。
这里的权衡是时间与价值。 您将拥有多少数据/更改频率/更改谁将是您必须回答的问题。
你可以编写一个带有状态的“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()
另一种方法是用一些简单的格式发明一个简单的配置文件/字符串。
嵌套结构可能是一个很好的选择。 不要暴露孩子名单的好主意。
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 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 Children { get { return _children.AsReadOnly(); } set { _children.Clear(); _children.AddRange(value); } } private List _children = new List (); }
您可以使用填充树的简单解析器编写树内容的简单XML表示。 以下将给出您在上面指定的结构。
Parent Child 1 Grandchild 1 Child 2
我会将实现拆分为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); } }
主要思想是您要返回刚刚添加到树中的节点。
你可能也想让孩子知道它的父母,因为这有时候非常方便。