Reputation: 6403
I'm currently trying to write a simple compiler for even simpler language, but I have problem with adding the Visitor
pattern.
I have a ILanguageVisitor
interface that looks like this:
interface ILanguageVisitor {
void Visit(GlobalStructure cs);
void Visit(GotoStructure cs);
void Visit(IfStructure cs);
void Visit(ElseIfStructure cs);
void Visit(ElseStructure cs);
...
}
All of these methods must be implement to create concrete visitor for specific architecture. But above this, there are some methods and fields that should be common for all possible visitors.
That is for example functionality of blocks, which consist of two or more Visit
calls, like:
Visit(If)
...
Visit(Else)
...
Visit(EndIf)
Visit(For)
...
Visit(EndFor)
This is because of the rules of block start and termination (like there can't be two else
s in one block, or that child block can't be terminated by parent as in case For ... If ... EndFor
).
My question is: if I have a behavior that should be common for all Visitor
s, should I create an abstract Visitor class, which would make these specific methods virtual
and other abstract
?
Will be there any loss of Visitor
´s point if I add a default behavior to its base?
Upvotes: 3
Views: 1411
Reputation: 4778
As for me the visitor pattern is a smart If-Else construction, take a look on this solution
public static class Visitor
{
public static IFuncVisitor<TBase, TResult> For<TBase, TResult>()
where TBase : class
{
return new FuncVisitor<TBase, TResult>();
}
public static IActionVisitor<TBase> For<TBase>()
where TBase : class
{
return new ActionVisitor<TBase>();
}
private sealed class ActionVisitor<TBase> : IActionVisitor<TBase>
where TBase : class
{
private readonly Dictionary<Type, Action<TBase>> _repository =
new Dictionary<Type, Action<TBase>>();
public void Register<T>(Action<T> action)
where T : TBase
{
_repository[typeof(T)] = x => action((T)x);
}
public void Visit<T>(T value)
where T : TBase
{
Action<TBase> action = _repository[value.GetType()];
action(value);
}
}
private sealed class FuncVisitor<TBase, TResult> : IFuncVisitor<TBase, TResult>
where TBase : class
{
private readonly Dictionary<Type, Func<TBase, TResult>> _repository =
new Dictionary<Type, Func<TBase, TResult>>();
public void Register<T>(Func<T, TResult> action)
where T : TBase
{
_repository[typeof(T)] = x => action((T)x);
}
public TResult Visit<T>(T value)
where T : TBase
{
Func<TBase, TResult> action = _repository[value.GetType()];
return action(value);
}
}
}
public interface IFuncVisitor<in TBase, TResult>
where TBase : class
{
void Register<T>(Func<T, TResult> action)
where T : TBase;
TResult Visit<T>(T value)
where T : TBase;
}
public interface IActionVisitor<in TBase>
where TBase : class
{
void Register<T>(Action<T> action)
where T : TBase;
void Visit<T>(T value)
where T : TBase;
}
Usage example:
IActionVisitor<Letter> visitor = Visitor.For<Letter>();
visitor.Register<A>(x => Console.WriteLine(x.GetType().Name));
visitor.Register<B>(x => Console.WriteLine(x.GetType().Name));
Letter a = new A();
Letter b = new B();
visitor.Visit(a);
visitor.Visit(b);
Where Letter
is the base class for A
and B
Upvotes: 2
Reputation: 20044
Design patterns are solutions for design problems, not implementation rules. So if you choose to implement a visitor pattern using interfaces, abstract classes, or both (like @AdiLester suggested) is completly up to you
If I remember correctly, the original GOF book used a lot of C++ exmples, where there is not even a language element interface.
Upvotes: 1
Reputation: 25201
In these kind of cases, I believe the best solution is to have both an interface and a base class which implements this interface and allows users to override certain methods. This way, the user can decide whether he'd like to:
Upvotes: 2