我有一些“输入”可以通过多种不同的方式进行解析。
我正在尝试从需要解析结果的各种下游引擎中抽象出解析代码(某些部分将显示它,其他部分实际执行它等等。)
到目前为止,我有这个:
interface IParsedNode
{
// Visits this node, returns whatever the appropriate IVisitor function
// returned.
T Visit<T>(IVisitor<T> visitor);
}
interface IVisitor<T>
{
T Load(Aspect aspect);
T LoadFile(string group, string filename);
T Process(PluginProperties pluginProperties, IParsedNode input);
T Aggregate(List<IParsedNode> children);
T Rename(string newName, string oldName, IParsedNode input);
T Filter(string keep, IParsedNode input);
T Cache(IParsedNode input);
}
class Cached : IParsedNode
{
private readonly IParsedNode input;
public Cached(IParsedNode input)
{
this.input = input;
}
#region IParsedNode Members
public T Visit<T>(IVisitor<T> visitor)
{
return visitor.Cache(input);
}
#endregion
}
class Filter : IParsedNode
{
private readonly string keep;
private readonly IParsedNode input;
public Filter(string keep, IParsedNode input)
{
this.keep = keep;
this.input = input;
}
#region IParsedNode Members
public T Visit<T>(IVisitor<T> visitor)
{
return visitor.Filter(keep, input);
}
#endregion
}
等
正如你所看到的,这允许我有一个完全“抽象”的解析树,这也是类型安全的。我也喜欢一切都是不可改变的事实。
我抽象出“T”的类型,因为不同的下游系统将依次从抽象的解析树中创建自己的具体图形。
例如,这是IVisitor的一个实现:
// Used to convert the abstract tree into an actual tree that can then be post-processed.
class NodeTreeBuilder : IVisitor<Node>
{
private readonly NodeFactory nodeFactory;
public NodeTreeBuilder(NodeFactory nodeFactory)
{
this.nodeFactory = nodeFactory;
}
#region IVisitor<Node> Members
public Node Load(Aspect aspect)
{
return nodeFactory.CreateRaw(aspect);
}
public Node LoadFile(string group, string filename)
{
return nodeFactory.CreateFile(group, filename);
}
public Node Process(PluginProperties pluginProperties, IParsedNode input)
{
ProcessInfo processInfo = new ProcessInfo();
processInfo.AssemblyPath = pluginProperties.AssemblyPath;
processInfo.ClassName = pluginProperties.ClassName;
processInfo.Config = new PluginConfig(pluginProperties.Config, pluginProperties.HashConfig, pluginProperties.DeltaType);
PluginInfo pluginInfo = Registry.CreatePluginInfo(pluginProperties.Id, processInfo);
return nodeFactory.CreatePostProcess(pluginInfo, input.Visit(this), pluginProperties.RunOnEmpty);
}
public Node Aggregate(List<IParsedNode> children)
{
Node[] convertedChildren = children.ConvertAll<Node>(delegate(IParsedNode child) { return child.Visit(this); }).ToArray();
return nodeFactory.CreateAggregated(convertedChildren);
}
public Node Rename(string newName, string oldName, IParsedNode input)
{
return nodeFactory.Rename(oldName, newName, input.Visit(this));
}
public Node Filter(string keep, IParsedNode input)
{
return nodeFactory.Filter(keep, input.Visit(this));
}
public Node Cache(IParsedNode input)
{
return input.Visit(this).Cache(true);
}
#endregion
}
对于IVisitor的实际具体实现,这一切都比我希望的更好。
然而,实现IParsedNode本身(正如你最初看到我做的那样)证明有点单调乏味。我提供了一大堆只包含一个方法的接口实现....这让我想到也许我可以使用委托来减少膨胀:
class ParsedNode : IParsedNode
{
delegate T NodeType<T>(IVisitor<T> visitor);
private readonly NodeType nodeType;
public ParsedNode<T>(NodeType<T> nodeType)
{
this.nodeType = NodeType;
}
public T Visit<T>(IVisitor<T> visitor)
{
return nodeType(visitor);
}
}
但上面没有编译。我是否没有办法在某种通用委托方面实现IParsedNode?如果有办法让这项工作变得不那么冗长,那就太好了。
也许如果IParsedNode接口本身只是一个委托,这可能会起作用吗?
答案 0 :(得分:1)
我建议阅读Judith Bishop(C# 3.0 Design Patterns的作者)题为On the Efficiency of Design Patterns Implemented in C# 3.0的论文。
她使用委托专门处理访问者模式,并介绍基本实现(至少在算法上)。她的实施非常快,非常灵活。
这是我最喜欢的C#中访问者模式的实现,到目前为止。