The Dreaded Switch Statement

by breeve 27. January 2010 15:21

One of the quickest ways to bring a delightful code review to a halt is to slip in a big switch statement somewhere in the code. Upon encountering it, some will feel uncomfortable and not know why while others will give you a long soul piercing stare.

Switch statements get a bad rap for a number of reasons. The chief among them being when a value is added to an enum you have to track down every switch statement that is using that enum and add your new code. This may seem simple but most of the time there is more than one switch statement to contend with. Often the changes needed are in unfamiliar code which leads to a long and error prone process.

There are alternatives thanks to object oriented programming and polymorphism. To understand such an approach, the C# code below shows a scenario with a switch statement. The class Rect takes an enum FillStyle as a property. In the Draw method it creates different brushes—depending on the FillStyle property—using a switch statement. Code like this is very common.

public enum FillStyle
{
    Solid,
    Gradient
}

public class Rect
{
    private Point _location;
    private Size _size;

    public Rect(Point location, Size size)
    {
        _location = location;
        _size = size;
        FillStyle = FillStyle.Solid;
        FillColor = Color.Green;
    }

    public FillStyle FillStyle { get; set; }
    public Color FillColor { get; set; }

    public void Draw(Graphics graphics)
    {
        Brush brush = null;
        Rectangle bounds = new Rectangle(_location, _size);
        switch (FillStyle)
        {
            case FillStyle.Solid:
                brush = new SolidBrush(FillColor);
                break;
            case FillStyle.Gradient:
                brush = new LinearGradientBrush(bounds, FillColor, Color.Red, LinearGradientMode.Horizontal);
                break;
            default:
                Debug.Fail("Not a valid FillStyle");
                break;
        }

        using (brush)
        {
            graphics.FillRectangle(brush, bounds);
        }
    }
}

An alternate and better approach is to use an abstract class with public static concrete instances. The primary motivation for this is the abstract class can masquerade as an easy to use enum. This way the code the API user has to write is exactly the same as before and most importantly just as simple. In fact, most users, will never notice they are working with an abstract class with singleton instances; to them it just looks like an enum.

In our case, an abstract FillStyle class is created with two public singleton instances that have the same names as our original enum values above. The FillStyle class has one abstract method called CreateBrush. Reworking the above example looks like the code below.

public abstract class FillStyle
{
    public static readonly FillStyle Solid = new SolidImpl();
    public static readonly FillStyle Gradient = new GradientImpl();

    protected FillStyle()
    {
    }

    public abstract Brush CreateBrush(Color color, Rectangle bounds);

    private class SolidImpl : FillStyle
    {
        public override Brush CreateBrush(Color color, Rectangle bounds)
        {
            return new SolidBrush(color);
        }
    }

    private class GradientImpl : FillStyle
    {
        public override Brush CreateBrush(Color color, Rectangle bounds)
        {
            return new LinearGradientBrush(bounds, color, Color.Red, LinearGradientMode.Horizontal);
        }
    }
}

public class Rect
{
    private Point _location;
    private Size _size;

    public Rect(Point location, Size size)
    {
        _location = location;
        _size = size;
        FillStyle = FillStyle.Solid;
        FillColor = Color.Green;
    }

    public FillStyle FillStyle { get; set; }
    public Color FillColor { get; set; }

    public void Draw(Graphics graphics)
    {
        Rectangle bounds = new Rectangle(_location, _size);

        using (Brush brush = FillStyle.CreateBrush(FillColor, bounds))
        {
            graphics.FillRectangle(brush, bounds);
        }
    }
}

The first thing you will notice is the Draw method on the Rect class is simple and adding new values to the FillStyle class has become substantially easier than the former traditional enum example because the code can be added in one central place. Gone is the frustrating experience of updating every switch statement in the entire code base. The introduced abstraction adds to the extensibility of the design. If a user wants to add a custom FillStyle—not originally anticipated by the API authors—they can by inheriting from FillStyle and overriding CreateBrush in the same way.

Tags:

Software

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About Me

I am a Principal Engineer with 13 years experience developing and releasing software products. I started developing in C/C++ then moved into .NET and C# and have tech lead multiple projects. I have developed products in Windows Forms, ASP.NET/MVC, Silverlight, and WPF. I currently reside in Austin, Texas.

Own Projects

Pickaxe - An easy to use web page scraper. If you know a little SQL and how basic CSS selectors work, no web scraping product will be easier to use.

Download Page

Source Code

Created ASP.NET MVC forum originally targeting home owner associations but now in use by an investor group.

http://vtss.brockreeve.com

A language for querying PGATour golf strokes.

http://pga.brockreeve.com/

Real time bidder for car ads demo

http://cargawk.com/

Simple front end tic tac toe

http://tictac.brockreeve.com/

Currently Reading