Visitor Pattern versus LINQ-style fluent syntax for tree traversal 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 pla开发者_JS百科ce (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?
The Visitor pattern helps you to avoid creating if
or switch
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 over your alternative approach any 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.
精彩评论