我正在构建一个树来解析一些文本,所以我创建了一些节点:
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;
}
}
我很快就会添加一些“分支”节点(包含其他节点的节点)。
我想知道处理它们的最佳方法。现在我有一些看起来像这样的代码:
foreach (var item in nodes)
{
if (item is TextNode)
sb.Append(((TextNode)item).Text);
if (item is VariableNode)
sb.Append(dict[((VariableNode)item).Name]);
}
这似乎有点笨拙,只会在我加入时变得更糟。
我应该
Node
类中,以便我可以只使用DoStuff()
函数而不必担心类型转换?需要给你们更多的背景信息。在上面的示例中,变量节点需要访问dict来呈现自己。
我正在解析模板。在模板中,有变量。变量名称及其值存储在dict中。在解析时,我没有值(好吧,我可以,但我希望能够使用不同的值重新呈现模板),因此我需要能够将不同的值传递给模板。我设置它的方式,模板完成所有渲染,节点什么都不做。我想我可以将dict更多级别传递给每个节点,以便他们自己渲染...
详细说明#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();
}
}
我想这也不是一个糟糕的解决方案。
到目前为止的解决方案:
答案 0 :(得分:2)
您可以改为使用interface
并定义enum
属性,例如。 NodeType Type
其中Type
是enum
,其值为TextNode
,VariableNode
。虽然,在这里您还需要强制转换节点以获取值。您可以尝试添加名为Value
的属性,该属性会返回Text
或Name
,具体取决于NodeType
。如果dict
可以访问VariableNode
,则Value
可以返回您的方法所需的确切值。
总而言之,我认为界面比抽象类更适合您的要求。
答案 1 :(得分:2)
如果您将要有许多具有不同行为的不同节点类型,我当然会考虑使用基类Node,然后为实现特定行为的每种类型实现专门的节点类。
将树处理逻辑放入节点的替代方法是使用visitor pattern。在这种情况下,您将倾向于使用更加同质的树结构,并在树外维护特定的处理规则。
如果此树是用于解析文本的,您是否查看了Antlr?此工具可以根据指定的语法为您生成语法树。
答案 2 :(得分:2)
将foreach(节点中的var项)移动到抽象节点类中的方法(即BuildAll)中,创建抽象/虚方法Build(在抽象Node类中)并在BuildAll中调用方法Build()并让它输出一个字符串......
像这样:
public abstract string Build();
public string BuildAll() {
var output = new StringBuilder();
foreach(var node in nodes) {
output.append(node.Build());
}
return output.toString();
}
并在节点的每个实际实现中覆盖它... 所以TextNode会......
public override string Build() {
return ... extract whatever you want from TextNode;
}
在这种情况下,您的基类不需要知道其后代的确切实现,因此不会破坏开放关闭原则。
答案 3 :(得分:1)
多态性正是你想要的。像这样:
class Node
{
public virtual string ToDisplayText(params object[] parameters)
{
return string.Empty;
}
}
class TextNode : Node
{
public override string ToDisplayText(params object[] parameters)
{
return this.Text;
}
}
class VariableNode : Node
{
public override string ToDisplayText(params object[] parameters)
{
//check parameters
var dict = (Dictionary<string,string>)parameters[0];
return dict[this.Name];
}
}
所以你可以:
foreach(var node in nodes)
{
sb.Append(node.ToDisplayText(dict));
}
答案 4 :(得分:1)
您可以创建Dictionary<Type,Action<T>>
- 密钥是节点类型,Action<T>
delegates是您对每个密钥执行的操作。
你可以这样做:
Dictionary[typeof(variable)];
为每种类型执行正确的操作。