树遍历 API 的访问者模式与 LINQ 风格的流畅语法
我正在考虑重构一个开源项目 Afterthought,以使其使用起来更加直观。基本思想是,在 Afterthought 中创建修改的开发人员将修改特定的 .NET 类型,并有机会修改类型本身以及属性、方法、事件、构造函数等的列表(树)。我在内部使用的 API Microsoft CCI 元数据 在其 API 中广泛使用了访问者模式,因此我采用了类似的方法Afterthought 中的方法,如下所示:
public override void Amend()
{
// Amend the type here, add properties, add methods, etc.
}
public override void Amend<TProperty>(Property<TProperty> property)
{
// Amend properties here
if (property.Name == "Result")
{
// Modify Result property
}
}
public override void Amend(Method method)
{
// Amend methods here
if (method.Name == "Add")
{
// Modify Add method
method.Implement(TInstance instance, int x, int y) => x + y);
}
}
但是,我发现访问者模式实际上最终将解决目标问题的代码(例如检测类库)重新分配为一系列专注于树的各个方面的不同方法。对于创建 API 的开发人员来说,这很容易实现,但消费者必须以某种不自然的方式分散他们的代码。所以我提出了一个问题,与仅将树公开为列表并利用 LINQ 风格的方法相比,访问者模式有什么好处?
以下是我正在考虑的替代语法:
public override void Amend()
{
// Do everything here, possibly calling methods just to organize the code
// Modify Add method
Methods.Named("Add").WithParams<int, int>()
.Implement((instance, x, y) => x + y);
}
因此,在这种情况下,修正案的作者可以通过与公开流利/LINQ API 的列表进行交互,而不是覆盖方法,将所有代码编写在一个位置(或他们选择的位置) 。显然,这种方法的性能稍差(更多迭代等),但除此之外,还有哪些缺点?
I am considering refactoring an open source project, Afterthought, to make it more intuitive to use. The basic idea is that developers creating amendments in Afterthought will be amending a specific .NET type and given the opportunity to modify the type itself, and lists of properties, methods, events, constructors, etc. (the tree). The API's I use internally Microsoft CCI Metadata make extensive use of the visitor pattern in their API's, so I adopted a similar approach in Afterthought, as follows:
public override void Amend()
{
// Amend the type here, add properties, add methods, etc.
}
public override void Amend<TProperty>(Property<TProperty> property)
{
// Amend properties here
if (property.Name == "Result")
{
// Modify Result property
}
}
public override void Amend(Method method)
{
// Amend methods here
if (method.Name == "Add")
{
// Modify Add method
method.Implement(TInstance instance, int x, int y) => x + y);
}
}
However, I have discovered that the visitor pattern really ends up redistributing the code solving the target problem (such as instrumenting a class library) into a series of different methods focused on aspects of the tree. This is easy to implement for the developer creating the API, but the consumer must spread their code out in a somewhat unnatural way. So I pose the question, what are the benefits to the vistor-pattern over just exposing the tree as lists and leveraging a LINQ-style approach?
Here is the alternative syntax I am considering:
public override void Amend()
{
// Do everything here, possibly calling methods just to organize the code
// Modify Add method
Methods.Named("Add").WithParams<int, int>()
.Implement((instance, x, y) => x + y);
}
So in this case, the author of an amendment can write all of the code in one place (or places of their choosing) by interacting with lists that expose a fluent/LINQ API instead of overriding methods. Obviously this approach is slightly less performant (more iterations, etc.), but outside of this, what are the downsides?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
访问者模式可帮助您避免创建用于测试被访问元素类型的
if
或switch
语句。避免这些陈述通常被认为是一种良好做法。如果您实现的操作无法区分可访问的各种可能类型的元素,那么,是的,访问者模式不会为您带来任何优于您的替代方法任何其他方法的优势。问题可能出在对访问者模式的理解上:它主要是在不提供此功能的语言中实现双重调度,或者(这是 C# 的情况)使用双重调度会带来相当大的性能问题。这与树遍历无关。它甚至可以用于不形成层次结构的类。 GoF 书中说,遍历算法可以由访问者本身实现,也可以由被访问的元素实现,甚至可以由客户端实现。
编辑:我再次仔细阅读了您的问题,我认为您的方法是,比方说,访问者模式的另一种实现,其中访问者不是一个类,而是一组 lambda 函数。
The Visitor pattern helps you to avoid creating
if
orswitch
statements that will test the type of the visited element. The avoidance of these statements is generally considered as a good practice. If the operation you implement does not differentiate between various possible types of elements that can be visited, then, yes, the visitor pattern does not bring you any advantage overyour alternative approachany other approach.The problem is maybe in the understanding of the Visitor pattern: it is mainly about implementing the double-dispatch in languages that does not offer this feature, or (this is the case of C#) where the usage double-dispatch brings considerable performance issues. It is not about tree traversing. It can be even used for classes that does not form a hierarchical structure. The GoF book says that the traversing algorithm may be implemented either by the Visitor itself, either by the visited elements, or even either by the client.
Edit: I read you question again carefully, I think that your approach is, let's say, an alternative implementation of the Visitor pattern, where the Visitor is not a class, but set of lambda functions.