65.9K
CodeProject is changing. Read more.
Home

Dynamic Visitor Pattern

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (17 votes)

Mar 17, 2013

CPOL

2 min read

viewsIcon

54385

downloadIcon

218

Visitor design pattern - dynamic implementation.

Introduction   

Visitor “represent[s] an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates” (GoF).

I believe you know the Visitor design pattern, so here is dynamic implementation. The dynamic visitor separates the pattern from the business logic. The visitor pattern is based on double-dispatch, so we should implement runtime dispatching. We could do this one  for example thru dynamic or Type.

Following class hierarchy is used in all examples 

 

Visitor based on "Dynamic

The dynamic type is resolved at runtime. By specify concrete type to dynamic type we can dispatch to concrete action.   

The code consists of several classes:

  • Visitor - factory for visitor
  • IActionVisitor<in TBase> - visitor's interface 

IActionVisitor<in TBase>  -  lets you register Action<T> on concrete type 

public interface IActionVisitor<in TBase>
        where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Action<T> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    void Visit<T>(T value)
        where T : TBase;
}  

Visitor - factory for IActionVisitor<in TBase>  visitor.   

public static class Visitor
{
    /// <summary>
    /// Create <see cref="IActionVisitor{TBase}"/>.
    /// </summary>
    /// <typeparam name="TBase">Base type.</typeparam>
    /// <returns>New instance of <see cref="IActionVisitor{TBase}"/>.</returns>
    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, dynamic> _repository =
            new Dictionary<Type, dynamic>();

        public void Visit<T>(T value)
            where T : TBase
        {
            dynamic action = _repository[value.GetType()];
            action((dynamic)value);
        }

        public void Register<T>(Action<T> action)
            where T : TBase
        {
            _repository[typeof(T)] = action;
        }
    }
}  

The magic happens here 

private readonly Dictionary<Type, dynamic> _repository = new Dictionary<Type, dynamic>(); 

and here 

    public void Visit<T>(T value)
        where T : TBase
    {
        dynamic action = _repository[value.GetType()];
        action((dynamic)value);
    } 

DLR (dynamic language runtime) resolves type and executes concrete action. 

Example 

    private static void DynamicActionVisitor()
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        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);

        stopwatch.Stop();
        Console.WriteLine("Execution time: {0} ms", stopwatch.Elapsed.TotalMilliseconds);
    }  

Here is the result of running  

 

First call with runtime magic is too expensive, so the Visitor based on dynamic is useful only with many calls. 

Visitor based on "Type

By specify concrete type to Action<T> we can dispatch to concrete action.

The code consists of several classes:

  • Visitor - factory for visitor
  • IActionVisitor<in TBase> - visitor's interface 

IActionVisitor<in TBase>  -  lets you register Action<T> on concrete type 

public interface IActionVisitor<in TBase>
        where TBase : class
{
    /// <summary>
    /// Register action on <see cref="T"/>.
    /// </summary>
    /// <typeparam name="T">Concrete type.</typeparam>
    /// <param name="action">Action.</param>
    void Register<T>(Action<T> action)
        where T : TBase;

    /// <summary>
    /// Visit concrete type.
    /// </summary>
    /// <param name="value">Type to visit.</param>
    void Visit<T>(T value)
        where T : TBase;
}  

Visitor - factory for IActionVisitor<in TBase>  visitor.   

public static class Visitor
{
    /// <summary>
    /// Create <see cref="IActionVisitor{TBase}"/>.
    /// </summary>
    /// <typeparam name="TBase">Base type.</typeparam>
    /// <returns>New instance of <see cref="IActionVisitor{TBase}"/>.</returns>
    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);
        }
    }
}   

The magic is here, we create new Action<TBase> and do not lose the concrete type 

        public void Register<T>(Action<T> action)
            where T : TBase
        {
            _repository[typeof(T)] = x => action((T)x);
        } 

and here.  We use value.GetType()  for resolving object type in runtime 

         public void Visit<T>(T value)
            where T : TBase

        {
            Action<TBase> action = _repository[value.GetType()];
            action(value);
        } 

Example 

    private static void DynamicActionVisitor()
    {
        Stopwatch stopwatch = Stopwatch.StartNew();

        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);

        stopwatch.Stop();
        Console.WriteLine("Execution time: {0} ms", stopwatch.Elapsed.TotalMilliseconds);
    }  

Here is the result of running

 

Visitor based on "Type" - Full version  

IActionVisitor<in TBase>, IFuncVisitor<in TBase, TResult>   -  lets you register Action<T>, Func<T, TResult> on concrete type  

    public interface IFuncVisitor<in TBase, TResult>
        where TBase : class
    {
        /// <summary>
        /// Register action on <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">Concrete type.</typeparam>
        /// <param name="action">Action.</param>
        void Register<T>(Func<T, TResult> action)
            where T : TBase;

        /// <summary>
        /// Visit concrete type.
        /// </summary>
        /// <param name="value">Type to visit.</param>
        /// <returns>Result value.</returns>
        TResult Visit<T>(T value)
            where T : TBase;
    }
    public interface IActionVisitor<in TBase>
        where TBase : class
    {
        /// <summary>
        /// Register action on <see cref="T"/>.
        /// </summary>
        /// <typeparam name="T">Concrete type.</typeparam>
        /// <param name="action">Action.</param>
        void Register<T>(Action<T> action)
            where T : TBase;

        /// <summary>
        /// Visit concrete type.
        /// </summary>
        /// <param name="value">Type to visit.</param>
        void Visit<T>(T value)
            where T : TBase;
    } 

Visitor - factory for IFuncVisitor<in TBase, TResult>, IActionVisitor<in TBase>  visitors. 

    public static class Visitor
    {
        public static IFuncVisitor<T, TResult> For<T, TResult>()
            where T : class
        {
            return new FuncVisitor<T, TResult>();
        }

        public static IActionVisitor<T> For<T>()
            where T : class
        {
            return new ActionVisitor<T>();
        }

        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);
            }
        }
    } 

Conclusion 

That's all for now, I hope you enjoyed it, please take the time to post a comment. Thanks for reading the article. 

History 

  • 17th March, 2013: Initial version.
  • 03d  May, 2013: Added Visitor based on Type