Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strategy Pattern with no 'switch' statements?

I've been doing some reading on the Strategy Pattern, and have a question. I have implemented a very basic Console Application below to explain what I'm asking.

I have read that having 'switch' statements is a red flag when implementing the strategy pattern. However, I can't seem to get away from having a switch statement in this example. Am I missing something? I was able to remove the logic from the Pencil, but my Main has a switch statement in it now. I understand that I could easily create a new TriangleDrawer class, and wouldn't have to open the Pencil class, which is good. However, I would need to open Main so that it would know which type of IDrawer to pass to the Pencil. Is this just what needs to be done if I'm relying on the user for input? If there's a way to do this without the switch statement, I'd love to see it!

class Program {     public class Pencil     {         private IDraw drawer;          public Pencil(IDraw iDrawer)         {             drawer = iDrawer;         }          public void Draw()         {             drawer.Draw();         }     }      public interface IDraw     {         void Draw();     }      public class CircleDrawer : IDraw     {         public void Draw()         {             Console.Write("()\n");         }     }      public class SquareDrawer : IDraw     {         public void Draw()         {             Console.WriteLine("[]\n");         }     }      static void Main(string[] args)     {         Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure");          int input;         if (int.TryParse(Console.ReadLine(), out input))         {             Pencil pencil = null;              switch (input)             {                 case 1:                     pencil = new Pencil(new CircleDrawer());                     break;                 case 2:                     pencil = new Pencil(new SquareDrawer());                     break;                 default:                     return;             }              pencil.Draw();              Console.WriteLine("Press any key to exit...");             Console.ReadKey();         }     } } 

Implemented Solution shown below (Thanks to all who responded!) This solution got me to the point where the only thing I need to do to use a new IDraw object is to create it.

public class Pencil     {         private IDraw drawer;          public Pencil(IDraw iDrawer)         {             drawer = iDrawer;         }          public void Draw()         {             drawer.Draw();         }     }      public interface IDraw     {         int ID { get; }         void Draw();     }      public class CircleDrawer : IDraw     {          public void Draw()         {             Console.Write("()\n");         }          public int ID         {             get { return 1; }         }     }      public class SquareDrawer : IDraw     {         public void Draw()         {             Console.WriteLine("[]\n");         }          public int ID         {             get { return 2; }         }     }      public static class DrawingBuilderFactor     {         private static List<IDraw> drawers = new List<IDraw>();          public static IDraw GetDrawer(int drawerId)         {             if (drawers.Count == 0)             {                 drawers =  Assembly.GetExecutingAssembly()                                    .GetTypes()                                    .Where(type => typeof(IDraw).IsAssignableFrom(type) && type.IsClass)                                    .Select(type => Activator.CreateInstance(type))                                    .Cast<IDraw>()                                    .ToList();             }              return drawers.Where(drawer => drawer.ID == drawerId).FirstOrDefault();         }     }      static void Main(string[] args)     {         int input = 1;          while (input != 0)         {             Console.WriteLine("What would you like to draw? 1:Circle or 2:Sqaure");              if (int.TryParse(Console.ReadLine(), out input))             {                 Pencil pencil = null;                  IDraw drawer = DrawingBuilderFactor.GetDrawer(input);                  pencil = new Pencil(drawer);                  pencil.Draw();             }         }     } 
like image 954
JSprang Avatar asked Sep 30 '10 19:09

JSprang


People also ask

What can I use instead of a switch statement?

Luckily, JavaScript's object literals are a pretty good alternative for most switch statement use-cases I can think of. The idea is to define an object with a key for each case you would have in a switch statement. Then you can access its value directly using the expression you would pass to the switch statement.

Should switch statements be avoided?

Therefore nested switch statements should be avoided. Specifically, you should structure your code to avoid the need for nested switch statements, but if you cannot, then consider moving the inner switch to another function.

What type of strategy is a pattern?

In Strategy pattern, a class behavior or its algorithm can be changed at run time. This type of design pattern comes under behavior pattern. In Strategy pattern, we create objects which represent various strategies and a context object whose behavior varies as per its strategy object.


2 Answers

Strategy isn't a magic anti-switch solution. What it does do is give modularise your code so that instead of a big switch and business logic all mixed up in a maintenance nightmare

  • your business logic is isolated and open for extension
  • you have options as for how you create your concrete classes (see Factory patterns for example)
  • your infrastructure code (your main) can be very clean, free of both

For example - if you took the switch in your main method and created a class which accepted the command line argument and returned an instance of IDraw (i.e. it encapsulates that switch) your main is clean again and your switch is in a class whose sole purpose is to implement that choice.

like image 178
brabster Avatar answered Sep 24 '22 00:09

brabster


The following is an over engineered solution to your problem solely for the sake of avoiding if/switch statements.

CircleFactory: IDrawFactory {   string Key { get; }   IDraw Create(); }  TriangleFactory: IDrawFactory {   string Key { get; }   IDraw Create(); }  DrawFactory {    List<IDrawFactory> Factories { get; }    IDraw Create(string key)    {       var factory = Factories.FirstOrDefault(f=>f.Key.Equals(key));       if (factory == null)           throw new ArgumentException();       return factory.Create();    } }  void Main() {     DrawFactory factory = new DrawFactory();     factory.Create("circle"); } 
like image 34
Muhammad Hasan Khan Avatar answered Sep 24 '22 00:09

Muhammad Hasan Khan