Strategy Pattern: Switch Statements, Begone!

Strategy Pattern: Switch Statements, Begone!


Switch statements seem like a pretty convenient tool for handling control flow, and it certainly has its places. In my experience, however, switch statements (more often than not) end up leading to code smells. They gradually become parts of your code base that developers actively avoid. Why is that?


  
switch (switchVar)
{
    case "1":
        // A ton of code
    case "2":
        // Some different code
        break;
    case "3":
        // Some more code
        break;
    case "4":
        // This method is getting long
        break;
    case "5":
        // More code
        break;
    .
    .
    .
    case "100":
        // Heeelp
        break;
}

This is because switch statements make it very easy to extend functionality in an un-maintainable way. Whenever a new business case comes up, another switch case can be added to this already long function. As time goes on, testing and maintaining this switch statement becomes more and more of a burden. One way I've managed to avoid this in my projects is by implementing the Strategy pattern to handle this. I've implemented this particular flavor of a Strategy Pattern many times in multiple personal and professional projects, and it always leads me to nice, maintainable, easy to read code.

A Quick Example - Element Renderers


Let's say you're working on a desktop application that needs to render different element types on your Window/Grid/whatever. Each element type has its own logic. Now, an initial idea might be to use a switch statement. Maybe something like 
  
switch (element.ElementType)
{
    case ElementType.Button:
        // Render Button Code
        break;
    case ElementType.Label:
        // Render Label Code
        break;
    case ElementType.Image:
        // Render Image Code
        break;
}


This seems pretty okay, but it's easy to see that as more element types get added to your system, the messier this will become. To avoid this altogether, let's break this up into separate classes!

ElementRender - The Base Interface

public interface IElementRenderer
{
    bool CanRender(Element element);
    void Render(Element element);
}

This interface is pretty simple. It only has two public methods. A bool to determine whether or not it would be able to render this element, and an actual Render method that does the work. Some implementations of this interface would look like

public class ButtonElementRenderer : IElementRenderer
{
    public bool CanRender(Element element)
    {
        return element.ElementType == ElementType.Button;
    }

    public void Render(Element element)
    {
        // Render Button Logic
    }
}

public class LabelElementRenderer : IElementRenderer
{
    public bool CanRender(Element element)
    {
        return element.ElementType == ElementType.Label;
    }

    public void Render(Element element)
    {
        // Render Label Logic
    }
}

public class ImageElementRenderer : IElementRenderer
{
    public bool CanRender(Element element)
    {
        return element.ElementType == ElementType.Image;
    }

    public void Render(Element element)
    {
        // Render Image Logic
    }
}


Notice that there's one class per switch case, and the class is explicit about what kind of elements it can render (via the "CanRender" interface implementation). This makes each "case" much easier to unit test in isolation and change in isolation. These are the true advantages of breaking up your code this way. But, how do we actually replace the switch statement with these classes?

IoC - Inject Your Renderers

Every IoC container I've worked with allows you to register collections. Of course, you don't need to use a container if you don't want to. I'd argue that even if you were to maintain the list of renderers in your code explicitly, it'd end up being "better" than having a giant switch statement. However, assuming you're familiar with, say, SimpleInjector, then you can configure your container like this

    container.Register(typeof(IElementRenderer), new[] { typeof(IElementRenderer).Assembly });

What this one line of code will do is search your assembly for every class that implements IElementRenderer and add it to your container. When you have a constructor that takes in an IEnumerable, then all of your element renderers will be injected for you. Your constructor would then look something like
    
    
    IEnumerable<IElementRenderer> _renderers;
    public Window(IEnumerable<IElementRenderer> renderers)
    {
        _renderers = renderers;
    }

And now, for the method that used to be a giant switch statement, we have
    
    var renderer = _renderers.FirstOrDefault(renderer => renderer.CanRender(element));
    if (renderer == null) 
        throw new Exception($"No renderer found that can render element {element.ElementType.ToString()}");
    renderer.Render(element);

We search our _renderers for one that CanRender the element. If none is found, we throw a helpful exception to remind us to write a renderer for that type. At that point, we'll have a renderer capable of rendering the element we have, so now we just Render(element).

This pattern makes adding new element types satisfyingly simple. Once you have a new ElementType in your system, and it's time to write some rendering logic for it, all you have to do is implement the IElementRenderer interface, with the appropriate CanRender method defined. Once that renderer is implemented anywhere in your assembly, SimpleInjector (or whatever container you're using) will find it, add it to your _renderers collection, and it'll just work.

I hope you find this as useful as I did. This is probably my most used pattern as it's so versatile and easy to unit test and extend.

Happy Coding!

Comments

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Logisticguru Limited provide the fastest delivery of cars and many other vehicles safely and at affordable price. Typically their car transport in Greater Noida revolves around moving car from Pune to other state or city in India. Whether it is new car or old car, you can get same attention for all types of vehicle from them. If you have to go outside of Pune for job purpose, and then you need to relocate your car on same destination.

    ReplyDelete
  3. Our FREE Online Marathi Typing software uses Google transliteration typing service. After you type a word in English and hit a spacebar key, the word will be translated.

    ReplyDelete
  4. boxers western wear and have a group of very much experienced individuals attempting to give a tasteful edge to the items they have faith in giving the right answer for the present style needs and they comprehend the necessities they host fancy dresses ideal for the gathering, easygoing trip or a normal office day.

    ReplyDelete
  5. Best Laser Levels While there are times that merely eyeballing a straight line is sufficient, when precision counts, you need the help of a level to assure accuracy. And while a simple bubble level is good enough for hanging a picture, you’ll appreciate the benefits of a laser level for larger projects around your home and yard.

    ReplyDelete
  6. Nice informative post! Here is my blog where I share valuable information too, about typing education and writing. Please visit this website and Touch typing blog.

    ReplyDelete
  7. The tensile membranes structure is the term usually used to refer to the construction of roofs using a membrane held in place on steel cables. It is the form of structure that is carrying only tension, eliminating compression or bending.

    ReplyDelete
  8. We at Walletiya are concocting every one for the suggestion and solutions of the transactions. We as a whole realize that many people experience serious issues at whatever point they are intiating a transactions and they to know next to nothing whether or not the specific outlet accept the payment method or not.

    ReplyDelete
  9. Auditax are SMSF Auditing experts and provide superfund audit services to SMSF trustees and accountants throughout Australia. Online smsf audit

    ReplyDelete
  10. Las Vegas casino - JTM Hub
    All the hottest slot machines in town at the Bellagio Casino, a Las Vegas 울산광역 출장마사지 hotel. The Bellagio 영천 출장마사지 Casino is 춘천 출장안마 the epitome of excitement 오산 출장샵 with 제주도 출장마사지

    ReplyDelete
  11. The Stonemart" has been the corner stone of this unique mine to market online maretplace. The family owned business which has existed for more than 50 years has been supplying sandstone,china clay,quartz and other minerals wordwide 600x900 sandstone slabs

    ReplyDelete

Post a Comment

Popular posts from this blog

Communicating with your iOS app over USB (C# and/or Xamarin)

Creating a PayPal IPN Web API Endpoint