Replace conditional (VB.NET: Select-Case) with Strategy Pattern... HOW?!

 
555   99.9
Aug 12, 2010
 
Alright folks,... let's get to the point: Buzz words sometimes help to understand that there is something that needs to be improved. What might be even more helpful is a hint on HOW to actually improve something.

Like this one.

Anyone knows that a good developer follows the open-closed principle. Open to extension, closed to change. Select-Case in VB.NET for instance discounts this.

If you have something like:
Select Case customer.Type
  Case CustomerType.GoodCustomer
    ...
  Case CustomerType.BadCustomer
    ....
  Case Else
    .....
End Select

You will end up in having to change your existing implementation each time a new type of customer arises. The strategy pattern can help in implementing something to avoid this potential risk.

I took the example on this website: http://blogs.microsoft.co.il/blogs/gilf/archive/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements.aspx

It works pretty well. However,... now I need to change the implementation of the Context class to add new types to an internal Dictionary. Also it doesn't handle the Case Else.

Can anyone please introduce an example of how to apply the Strategy Pattern in a way that allows me to replace Conditionals with a construct that renders changes in existing code completely unnecessary and even handles Case Else?

That'd be awesome!
 1 comment
 
Could it be an appropriate way to use reflection to find all classes that inherit from my strategyclass, instantiate and add them dynamically to my dictionary at runtime? And if so,... how does this work? --- Christian Jacob  Aug 13, 2010



555   99.9
Jul 05, 2013
It's been some time, but in the meantime, I took this sample and wrote a little virtual lab that tries to explain how this stuff works. I also included Roberts' suggestion for a reflective way of introducing changeless context classes. Great solution, Robert!

If you like the documentation, comment on it. If anyone has suggestions or any criticism: Go for it. I love to improve my own work. :)

http://toptechlabs.codeplex.com

Cheers,
Chris.

1,128   99.9
Aug 15, 2010
This post is a continuation from the previous one, which shows how to get all classes that implements the IPassengerTitleStrategy and create and object of them and put them in a list which could be used to databind to a drop down list.

The following method will populate an IList whith implementations of type T.
private static IList<T> GetObjectsFrom<T>() {  
    Type strategyType = typeof (T);  
    List<Type> types = AppDomain.CurrentDomain.GetAssemblies()
                      .SelectMany(s => s.GetTypes())
                      .Where(p => strategyType.IsAssignableFrom(p) && p.IsClass).ToList();  
    return types.Select(type => (T) Activator.CreateInstance(type)).ToList();  
}  
This is how you can retrieve a list of objects from the classes that implements the interface IPassengerTitleStrategy:
private static void Main(string[] args) { 

  var types = GetObjectsFrom<IPassengerTitleStrategy>();  
  foreach (var passengerTitleStrategy in types) {  
    new Context(passengerTitleStrategy).DoAlgorithm();  
  }  
}

 
 3 comments
 
Hi Robert, in here in the Context class you made DepencyInjection ,am i right? IPassengerTitleStrategy _passengerTitleStrategy in this interface. I am confusing about these subject. --- Volkan Genç  Mar 15, 2011
 
Constructor injection --- Volkan Genç  Mar 15, 2011
 
Sorry for late reply, did not get notified of this coment. Yes you are correct, that is constructor injection. --- Robert Blixt  Mar 24, 2011

1,128   99.9
Aug 13, 2010
Hi,

This is interesting... I have taken a somewhat different approach to the
strategy pattern than what is presented on that page. First I have removed
the need for enums, as I try to avoid them if possible for maintainability concerns.

The context is now a full blown object using no statics.

The default behavior is initialized in the constructor, this should be equivalent
with the 'case else'.

To change the behavior of the context you provide a new IPassengerTitleStrategy
for the constructor. In this implementation it isn't possible to change behavior
after the object was created but that is easily fixed by adding a property to
the class.

Also, if you need new behavior just add another class that inherits from the
IPassengerTitleStrategy, there is no need to modify any enums or the Context
class at all.

Let me now if I completely missed the intent of you question :o) 


using System; 

namespace StrategyPattern 
{ 
    public class Program 
    { 
        private static void Main(string[] args) 
        { 
            var context = new Context(); 
            context.DoAlgorithm(); 

            context = new Context(new MrPassengerTitleStrategy()); 
            context.DoAlgorithm(); 
        } 

        #region Nested type: Context 

        public class Context 
        { 
            private readonly IPassengerTitleStrategy _passengerTitleStrategy; 

            public Context() 
            { 
                _passengerTitleStrategy = new RudePassengerTitleStrategy(); 
            } 

            public Context(IPassengerTitleStrategy passengerTitleStrategy) 
            { 
                _passengerTitleStrategy = passengerTitleStrategy; 
            } 

            public void DoAlgorithm() 
            { 
                _passengerTitleStrategy.DoAlgorithm(); 
            } 
        } 

        #endregion

        #region Nested type: DoctorPassengerTitleStrategy 

        public class DoctorPassengerTitleStrategy : IPassengerTitleStrategy 
        { 
            #region IPassengerTitleStrategy Members 

            public void DoAlgorithm() 
            { 
                Console.WriteLine("Dr."); 
            } 

            #endregion
        } 

        #endregion

        #region Nested type: IPassengerTitleStrategy 

        public interface IPassengerTitleStrategy 
        { 
            void DoAlgorithm(); 
        } 

        #endregion

        #region Nested type: MrPassengerTitleStrategy 

        public class MrPassengerTitleStrategy : IPassengerTitleStrategy 
        { 
            #region IPassengerTitleStrategy Members 

            public void DoAlgorithm() 
            { 
                Console.WriteLine("Mr."); 
            } 

            #endregion
        } 

        #endregion

        #region Nested type: MrsPassengerTitleStrategy 

        public class MrsPassengerTitleStrategy : IPassengerTitleStrategy 
        { 
            #region IPassengerTitleStrategy Members 

            public void DoAlgorithm() 
            { 
                Console.WriteLine("Mrs."); 
            } 

            #endregion
        } 

        #endregion

        #region Nested type: RudePassengerTitleStrategy 

        public class RudePassengerTitleStrategy : IPassengerTitleStrategy 
        { 
            #region IPassengerTitleStrategy Members 

            public void DoAlgorithm() 
            { 
                Console.WriteLine("#¤%&!"); 
            } 

            #endregion
        } 

        #endregion
    } 
} 

 
 3 comments
 
Sorry for not posting this in VB.Net (as stated in your title), I prefer C# :) --- Robert Blixt  Aug 13, 2010
 
I like this a lot, but my main issue with this is making that choice dynamically at runtime. In this example, my thought would be that the options would be in a drop down list. So I would like to see this solution modified a bit to handle that type of action. Also, your RudePassengerTitleStrategy response is spelled wrong. The ampersand (&amp;) should be a caret (^). Otherwise the response will have a completely different meaning. ;^) --- King Wilder  Aug 13, 2010
 
I would probably scan the assembly for any class that implements the IPassengerTitleStrategy interface and then add them all to a list which I will bind to a drop down list. I'll post some code on how to do that. --- Robert Blixt  Aug 15, 2010