I'm building up a tree to parse some text, so I've created some nodes:
abstract class Node { }
class TextNode : Node
{
public readonly string Text;
public TextNode(string text)
{
Text = text;
}
public TextNode(char ch)
{
Text = ch.ToString();
}
}
class VariableNode : Node
{
public readonly string Name;
public VariableNode(string name)
{
Name = name;
}
}
I'm going to add some "branching" nodes pretty soon (nodes that contain other nodes).
I'm wondering about the best way to process them. Right now I've got some code that looks like this:
foreach (var item in nodes)
{
if (item is TextNode)
sb.Append(((TextNode)item).Text);
if (item is VariableNode)
sb.Append(dict[((VariableNode)item).Name]);
}
Which seems a bit clunky, and will only get worse as I add to it.
Should I
- Try to encapsulate the logic into the base
Node
class somehow so that I can just theDoStuff()
function without worrying about type-casting? - Should I add some kind of enum for each node type so that I can use a switch instead of a foreach loop?
- Something else?
Need to give you guys a bit more context. In the example above, variables nodes need access to the dict to render themselves.
I'm parsing a template. In the template, there are variables. The variable names and their values are stored in the dict. At parse time, I don't have the values (well, I could, but I want to be able to re-render the template with different values), so I need to be able to pass in different dicts to the template. The way I have it set up, the template does all the rendering, the nodes do nothing. I guess I could pass the dict one more level down to each node so they can render themselves...
To elaborate on #1
// in class Template
public string Render(Dictionary<string, object> dict)
{
var sb = new StringBuilder();
foreach (var item in nodes)
sb.Append(item.Render(dict));
return sb.ToString();
}
interface INode {
string Render(Dictionary<string, object> dict);
}
class TextNode : INode
{
private string _text;
public TextNode(string text)
{
_text = text;
}
public TextNode(char ch)
{
_text = ch.ToString();
}
public string Render(Dictionary<string, object> dict)
{
return _text;
}
}
class VariableNode : INode
{
private string _name;
// TODO: filters...
public VariableNode(string name)
{
_name = name;
}
public string Render(Dictionary<string, object> dict)
{
return dict[_name].ToString();
}
}
I guess that isn't such a bad solution either.
Solutions so far:
- Use an enum and switch
- Build a dictionary of type/actions to make the switch a bit cleaner
- Polymorphism (x2)
- Visitor pattern (haven't read this yet)
- Use ANTLR -- although ANTLR will build a tree and then this problem applies again