Click here to Skip to main content
12,351,209 members (36,470 online)
Click here to Skip to main content
Add your own
alternative version

Stats

18.7K views
163 downloads
25 bookmarked
Posted

Dynamic Visitor Pattern

, 3 May 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
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 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sergey Morenko
Software Developer (Senior)
United States United States
B.Sc. in Computer Science.

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
DVL Patel2-Sep-15 18:57
professionalDVL Patel2-Sep-15 18:57 
GeneralRe: My vote of 5 Pin
Sergey Morenko2-Sep-15 23:35
professionalSergey Morenko2-Sep-15 23:35 
Generalnice Pin
joyhen12312312-Jul-15 16:01
memberjoyhen12312312-Jul-15 16:01 
GeneralRe: nice Pin
Sergey Morenko13-Jul-15 13:43
professionalSergey Morenko13-Jul-15 13:43 
GeneralMy vote of 5 Pin
Pranay Rana3-Feb-14 1:45
memberPranay Rana3-Feb-14 1:45 
GeneralRe: My vote of 5 Pin
Sergey Morenko4-Feb-14 9:50
professionalSergey Morenko4-Feb-14 9:50 
GeneralRe: My vote of 5 Pin
Pranay Rana4-Feb-14 12:31
memberPranay Rana4-Feb-14 12:31 
GeneralMy vote of 4 Pin
Paulo Zemek7-Jan-14 10:32
professionalPaulo Zemek7-Jan-14 10:32 
GeneralRe: My vote of 4 Pin
Sergey Morenko7-Jan-14 10:49
professionalSergey Morenko7-Jan-14 10:49 
GeneralMy vote of 4 Pin
JunRui3-May-13 19:19
memberJunRui3-May-13 19:19 
GeneralRe: My vote of 4 Pin
GSerjo4-May-13 0:34
memberGSerjo4-May-13 0:34 
QuestionNot an article Pin
OriginalGriff3-May-13 2:03
mvpOriginalGriff3-May-13 2:03 
AnswerRe: Not an article Pin
GSerjo3-May-13 2:29
memberGSerjo3-May-13 2:29 
GeneralRe: Not an article Pin
OriginalGriff3-May-13 3:39
mvpOriginalGriff3-May-13 3:39 
AnswerRe: Not an article Pin
GSerjo3-May-13 6:18
memberGSerjo3-May-13 6:18 
GeneralMy vote of 2 Pin
crood31-Mar-13 1:15
membercrood31-Mar-13 1:15 
GeneralRe: My vote of 2 Pin
GSerjo31-Mar-13 6:29
memberGSerjo31-Mar-13 6:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 3 May 2013
Article Copyright 2013 by Sergey Morenko
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid