views:

494

answers:

5

I have a Tree.

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>();
}

I would like to populate a large one for unit testing purposes. I would really like to keep stuff DRY.

Say for illustration purposes my tree has the following structure

Parent,desc 
  Child 1, desc1
    Grandchild 1, desc1 
  Child 2, desc2

How would you go about populating the tree in an elegant an maintainable way?

I find this code quite repetitious and error prone:

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);

EDIT

I ended up doing the DSL approach:

A demo test lives here.

The implementation is here.

It uses a builder and a simple DSL.

+2  A: 

You can write a "TreeBuilder" with a state to save some connections mess:

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()

Another way is to invent a simple configuration file/string with some simple format.

Untrots
+1  A: 

You could write up a simple XML representation of the tree contents with a simple parser that populates the tree. The following would give the structure you specified above.

<Node description="desc">
    Parent
    <Node description="desc1">
        Child 1
        <Node description="desc1">
            Grandchild 1
        </Node>
    </Node>
    <Node description="desc2">
        Child 2
    </Node>
</Node>
Snarfblam
+1. This is one of the only things XML is actually good for, since it's basically a human-readable tree form.
configurator
The big advantage of using XML over a DSL embedded in a string is the wealth of tools that surround it.
ICR
A: 

Nested construction could be a good option here. Good idea to not expose the list of children too.

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>();
}
mancaus
+2  A: 
  • Ideally you want a way to extend the language to literals of custom types. C# doesn't have this, so you have to find another approach.

  • You can fabricate an internal DSL, typically with a fluent interface.

  • Follow the XElement example of functional construction.

  • Create an external DSL with a custom parser. If you design the language carefully, the parser can be easy.

  • Use XML. Basically this is a way to create an external DSL and get the parser for free.

The external DSL options are nice because when you read them, you know there's only data, and don't have to worry about making sense of code constructs. Also, the data is the file, and the file is the data. That makes it easy to swap data around by changing files, and easier to ready file change histories. Finally, and external DSL is good when a non-programmer will be supplying the data.

The tradeoff here is time vs. value. How much data you will have / how often it will change / who will change it are the questions you have to answer.

Jay Bazuzi
+1  A: 

I would split the implementation into a TreeClass and a TreeNodeClass

The Tree Class would have member variables

TreeNodeClass root

with methods

TreeNodeClass addAtRoot(data)

Which return the node they just created

The TreeNodeClass Also needs an AddChild() Method, which would also return the node it just added.

Then you can do something like

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

or

Use something like this to randomly generate a tree

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

The main idea is that you want to return the Node that you just added to the tree.

You might also want to make the child know its parent as that can be quite handy sometimes.

JSmyth