views:

48

answers:

2

I'm new to design patterns, and thus have a limited knowledge of what all is available. Hopefully I can give some details around the issue I'm trying to solve, and the user community can give some guidance on what design pattern to use and how it should be implemented.

  1. Return object is the same for every type of call
  2. Underlying class implementations can be done on certain enum types (i.e. ActionType = 1 works fine with ActionClass1 but not ActionClass2 and ActionClass3
  3. Class parameters vary based on type

For example:

public enum ActionType
{
   Action1,
   Action2,
   Action3
}

Possible factory pattern implementation:

public static class ActionClass
{
        public static int DoAction(ActionType type, int val1, int val2)
        {
               switch (type)
               {
                  case Type1: 
                       return new ActionClass1(val1, val2).DoAction();
                       break;
                  default:
                       throw new NotImplementedException();                      
               }
         } 

         public static int DoAction(ActionType type, string val1)  
         {
             switch (type)
               {

                  case Type2: 
                       return new ActionClass2(val1).DoAction();
                       break;
                  case Type3:
                       return new ActionClass3(val1).DoAction();
                  default:
                        throw new NotImplementedException();                      
               }

         }
}    
+1  A: 

Depending on what your actions represent, you might want to have a look at the Strategy Pattern. Otherwise, the Factory Method Pattern looks already like a good fit, although I'd probably change the method to not execute the action immediately, but returns an object that encapsulates the action, and use the TryXXX pattern instead of exceptions when an action does not support the parameter types:

public static bool TryGetIntIntAction(
    ActionType type, out Func<int, int, int> func)
{
    switch (type)
    {
    case ActionType.Action1:
        func = (val1, val2) => new ActionClass1(val1, val2).DoAction();
        return true;
    default:
        func = null;
        return false;
    }
}

public static bool TryGetStringAction(
    ActionType type, out Func<string, int> func)
{
    switch (type)
    {
    case ActionType.Action2:
        func = val1 => new ActionClass2(val1).DoAction();
        return true;
    case ActionType.Action3:
        func = val1 => new ActionClass3(val1).DoAction();
        return true;
    default:
        func = null;
        return false;
    }
}
dtb
I like the idea of the Try methods. Clues the developer into the fact that it may nut return data. Can you explain what the func = (val1,val2) => syntax is?
Blake Blackwell
@Blake Blackwell: That's a lambda expression, a short-hand way to write a delegate. `func = ((val1,val2) => ...)` assigns a function to the variable `func` that takes two arguments (`val1` and `val2`) and returns `...`.
dtb
A: 

The usual way to refactor switches on types is polymorphism, by creating a class for each ActionType. Since each DoAction overload takes different parameters, you can encapsulate these in a 'parameter object' and associate each action type with its parameter type using generics:

public abstract ActionClass<T>
{
    public abstract int DoAction(T parameter);
}

public class ActionClass2 : ActionClass<string>
{
    public override int DoAction(string parameter) { ... }
}
Lee