Chris Weldon bio photo

Chris Weldon

A savvy software engineer and agilist, Chris slings code in C#, but has also been known for commanding fleets of systems. He's currently a Tech Lead at Wolters Kluwer.

Email Twitter Facebook Github

I attended Austin Code Camp a few weekends ago and had a blast! There were plenty of enthusiastic, passionate individuals gathered and ready to learn - the makings for a great one day conference. While there, I attended the session Must-Know Design Patterns by Rob Vettor. In that session, he discussed several very useful patterns, including the Decorator Pattern, the Factory Pattern, and numerous other useful patterns. In seeing his code samples for the factory pattern, I was a bit disappointed to see his entire block of if/else if/else statements simply moved from his business logic to the factory. While this is still technically the better approach than leaving it in business logic, I find there’s a better way to do this. In fact, there’s a zero case statement way of doing this in C#. If you’re curious to find out how to create a factory without a single if or switch statement, read on.

If you’ve never heard of it, check out the Anti If Campaign. I wasn’t aware of this until about a year ago, but prior to that I was already a practitioner of their preachings. The goal is simple: get rid of most (if not all) case statements in your code. Why? An increase in the number of case statements increases the branch (cyclomatic) complexity of your logic. You have to add more unit tests to ensure you hit everyone of those branches. It’s far too often the case that your code isn’t just a set of conditional statements - it has other logic surrounding it. It’s messy, and it’s poor design.

If you are truly paying attention to object-oriented design patterns, you’d realize there are ways for you to structure your code in a manner that minimizes or eliminates the need for conditional statements. Most people revolt when I make this assertion. Some of the arguments I’ve heard include:

  1. Won't you create too many classes to maintain? I have a hard enough time navigating my codebase as-is.
  2. Aren't classes "heavy"? Won't that bloat the application?
  3. How do I know what my application is doing without if/else if/else statements?

My responses are usually:

  1. You'll often create many more classes. But, if you have a) sound organizational plans and b) a somewhat-modern IDE you can find any of the classes you use. Learn to use your IDE so you can help you navigate your codebase.
  2. It depends on the class. Often, I create many extremely lightweight classes and only allow them to live while I need them to help keep memory consumption to a minimum.
  3. Are you flinging code at your screen like a monkey flings poo at others? How else do you expect to build your application? Understanding these (and other) design principles will allow you to make sense of what your app is doing, rather than treating it like some voodoo blackbox.

The Problem

Let’s take a look at a sample problem that I’d like to make much simpler. The code sample below should be fairly easy to follow. We are a custom factory responsible for building different makes and models of vehicles. As you can see, the majority of our business logic is tied up trying to figure out how to create the vehicle and assign it the options.

public class VehicleController
{
    public ActionResult buildVehicle(string make, string model, string color, int numDoors, string[] options = string[0])
    {
        Vehicle vehicle = null;
        if (make.Equals("Honda")) {
            if (model.Equals("Civic")) {
                // Ooh! My favorite!
                vehicle = new Civic();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("Accord")) {
                // Only comes in 4 doors. So, ignore numDoors.
                vehicle = new Accord();
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("CR-V")) {
                // Only comes in 4 doors. So, ignore numDoors.
                vehicle = new CRV();
                vehicle.Color = color;
                vehicle.Options = options;
            } else {
                throw new InvalidArgumentException("Cannot make requested model for Honda");
            }
        } else if (make.Equals("Chevrolet")) {
            if (model.Equals("Suburban")) {
                vehicle = new Suburban();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("Silverado")) {
                vehicle = new Silverado();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else {
                throw new InvalidArgumentException("Cannot make requested model for Chevrolet");
            }
        } else if (make.Equals("BMW")) {
            // Pompus luxury vehicle.
        } else if (make.Equals("Ferrari")) {
            // Not likely in my lifetime.
        } else if (make.Equals("Lamborghini")) {
            // I wish.
        }

        // Save.
        this.vehicleContext.Attach(vehicle);
        this.vehicleContext.SaveAll();

        // Display our newly created vehicle.
        return View("BuildVehicle", vehicle);
    }
}

The cyclomatic complexity of this action alone is ridiculous. Controller actions should be simple and straight-forward: collect data and validate, pass to business logic, return response. Any controller action over 20 LoC is doing too much and some refactoring should be considered. So, what should we do?

The Pattern

Let’s take the first step and refactor that gigantic if block into a separate factory:

public class VehicleFactory
{
    public Vehicle buildVehicle(string make, string model, string color, int numDoors, string[] options = string[0])
    {
        Vehicle vehicle = null;
        if (make.Equals("Honda")) {
            if (model.Equals("Civic")) {
                // Ooh! My favorite!
                vehicle = new Civic();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("Accord")) {
                // Only comes in 4 doors. So, ignore numDoors.
                vehicle = new Accord();
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("CR-V")) {
                // Only comes in 4 doors. So, ignore numDoors.
                vehicle = new CRV();
                vehicle.Color = color;
                vehicle.Options = options;
            } else {
                throw new InvalidArgumentException("Cannot make requested model for Honda");
            }
        } else if (make.Equals("Chevrolet")) {
            if (model.Equals("Suburban")) {
                vehicle = new Suburban();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else if (model.Equals("Silverado")) {
                vehicle = new Silverado();
                vehicle.Doors = numDoors;
                vehicle.Color = color;
                vehicle.Options = options;
            } else {
                throw new InvalidArgumentException("Cannot make requested model for Chevrolet");
            }
        } else if (make.Equals("BMW")) {
            // Pompus luxury vehicle.
        } else if (make.Equals("Ferrari")) {
            // Not likely in my lifetime.
        } else if (make.Equals("Lamborghini")) {
            // I wish.
        }

        return vehicle;
    }
}

This greatly simplifies our VehicleController (see below), but does little to solve our cyclomatic complexity problem. Furthermore, one of the biggest problems I have with this as a factory is it will have to be modified every time a business use case related to the factory changes. We need a better solution to help make this easier to maintain and unit test.

public class VehicleController
{
    public ActionResult buildVehicle(string make, string model, string color, int numDoors, string[] options = string[0])
    {
        VehicleFactory factory = new VehicleFactory();
        Vehicle vehicle = factory.buildVehicle(make, model, color, numDoors, options);

        // Save.
        this.vehicleContext.Attach(vehicle);
        this.vehicleContext.SaveAll();

        // Display our newly created vehicle.
        return View("BuildVehicle", vehicle);
    }
}

So, the first option I’d push for is moving the actual creation of each make into a separate factory. So, we’ll have a HondaFactory, a ChevroletFactory, and other factories for each of our other types. So, looking at the refactor for the HondaFactory, we have something similar to the following:

public class HondaFactory
{
    public Vehicle CreateVehicle(string model, string color, int numDoors, string[] options = string[0])
    {
        Vehicle vehicle = null;
        if (model.Equals("Civic")) {
            // Ooh! My favorite!
            vehicle = new Civic();
            vehicle.Doors = numDoors;
            vehicle.Color = color;
            vehicle.Options = options;
        } else if (model.Equals("Accord")) {
            // Only comes in 4 doors. So, ignore numDoors.
            vehicle = new Accord();
            vehicle.Color = color;
            vehicle.Options = options;
        } else if (model.Equals("CR-V")) {
            // Only comes in 4 doors. So, ignore numDoors.
            vehicle = new CRV();
            vehicle.Color = color;
            vehicle.Options = options;
        } else {
            throw new InvalidArgumentException("Cannot make requested model for Honda");
        }

        return vehicle;
    }
}

And here’s our ChevroletFactory:

public class ChevroletFactory
{
    public Vehicle CreateVehicle(string model, string color, int numDoors, string[] options = string[0])
    {
        Vehicle vehicle = null;
        if (model.Equals("Suburban")) {
            vehicle = new Suburban();
            vehicle.Doors = numDoors;
            vehicle.Color = color;
            vehicle.Options = options;
        } else if (model.Equals("Silverado")) {
                vehicle = new Silverado();
            vehicle.Doors = numDoors;
            vehicle.Color = color;
            vehicle.Options = options;
        } else {
            throw new InvalidArgumentException("Cannot make requested model for Chevrolet");
        }

        return vehicle;
    }
}

Now we need to figure out how to get our main VehicleFactory to route requests to each of these different factories without conditionals. But, before we do this, let’s think about what we just did. We need to realize that each of the factories we created are doing the same thing. When you start having several classes doing the same thing, it’s time to introduce an interface. With this, we also introduce what will be termed as the evaluator:

public interface IVehicleFactory
{
    Vehicle CreateVehicle(string model, string color, int numDoors, string[] options = string[0]);
    bool CanCreateMake(string make);
}

CanCreateMake evaluates the make and returns whether this factory can handle it. We then are able to change our HondaFactory to look like:

public class HondaFactory : IVehicleFactory
{
    public Vehicle CreateVehicle(string model, string color, int numDoors, string[] options = string[0])
    {
        Vehicle vehicle = null;
        if (model.Equals("Civic")) {
            // Ooh! My favorite!
            vehicle = new Civic();
            vehicle.Doors = numDoors;
            vehicle.Color = color;
            vehicle.Options = options;
        } else if (model.Equals("Accord")) {
            // Only comes in 4 doors. So, ignore numDoors.
            vehicle = new Accord();
            vehicle.Color = color;
            vehicle.Options = options;
        } else if (model.Equals("CR-V")) {
            // Only comes in 4 doors. So, ignore numDoors.
            vehicle = new CRV();
            vehicle.Color = color;
            vehicle.Options = options;
        } else {
            throw new InvalidArgumentException("Cannot make requested model for Honda");
        }

        return vehicle;
    }

    public bool CanCreateMake(string make)
    {
        return make.Equals("Honda");
    }
}

So, let’s store all of our vehicle factories into a collection in our primary VehicleFactory and pull out the one we want to use:

public class VehicleFactory
{
    private IList<IVehicleFactory> factories = new List<IVehicleFactory>();

    public VehicleFactory()
    {
        this.factories.Add(new HondaFactory());
        this.factories.Add(new ChevroletFactory());
    }

    public Vehicle buildVehicle(string make, string model, string color, int numDoors, string[] options = string[0])
    {
        IVehicleFactory usableFactory = this.factories.SingleOrDefault(fac => fac.CanCreateMake(make));
        if (usableFactory == null) throw new InvalidArgumentException("Cannot create vehicle of the requested make.");
        return usableFactory.CreateVehicle(model, color, numDoors, options);
    }
}

We’re definitely getting there, but there’s one glaring problem. As mentioned previously, I hate the way factories are usually implemented, as they require modification if we need to add a new conditional. We’re still in the same boat here. If we begin to manufacture a new make, we have to create that specific make’s factory and modify this central factory to register it. This breaks the Open/Closed Principle, and we need to fix it. Thankfully, our interfaces help.

At Lone Star PHP an individual approached me after my SOLID talk to mention that he’s not sold on the use of interfaces. He said that interfaces seem to be there only for the developers - as a means to help them add new classes that perform the same types of activities. But, he still didn’t see a computer-use for them. That’s when I busted out the use of Reflection. Reflection helps me walk my codebase and find objects of specific types without ever being aware of what is out there. Reflection will help us solve this problem.

public VehicleFactory()
{
    var discoveredFactories = Assembly.GetExecutingAssembly().GetTypes()
        .Where(type => type.GetInterfaces().Any(intrfce => intrfce.Equals(typeof(IVehicleFactory))));
    this.factories.AddRange(discoveredFactories);
}

This is now completely implementation agnostic and accomplishes our second goal: create a factory that’s safe against modifications - this factory is not likely to ever change. The added benefit that I’ve introduced is the ability to drop in any new make factory that implements IVehicleFactory, recompile, and deploy - and it will automatically be picked up by my assembly. That’s sexy.

Our final factory that all of our core business logic will use looks like:

public class VehicleFactory
{
    private IList<IVehicleFactory> factories = new List<IVehicleFactory>();

    public VehicleFactory()
    {
        var discoveredFactories = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => type.GetInterfaces().Any(intrfce => intrfce.Equals(typeof(IVehicleFactory))));
        this.factories.AddRange(discoveredFactories);
    }

    public Vehicle buildVehicle(string make, string model, string color, int numDoors, string[] options = string[0])
    {
        IVehicleFactory usableFactory = this.factories.SingleOrDefault(fac => fac.CanCreateMake(make));
        if (usableFactory == null) throw new InvalidArgumentException("Cannot create vehicle of the requested make.");
        return usableFactory.CreateVehicle(model, color, numDoors, options);
    }
}

Rinse and Repeat

Now that the basics have made themselves apparent, you should be able to figure out how to get rid of the remainder of the if/else blocks of this code. If you need further guidance, keep reading. Otherwise, I suggest you jump to the summary.

To get rid of the remainder of the if/else statements we have in our individual make factories, we apply the same principles that we did the first time around. We need yet another factory responsible for each model of the car. We then need a way to determine if that factory can handle the creation of the requested model. It’s time for yet another interface:

public interface IMakeVehicles
{
    Vehicle CreateVehicle(string color, int numDoors, string[] options = string[0]);
    bool CanCreateModel(string model);
}

So, let’s create our first vehicle, a Civic:

public class CivicFactory : IMakeVehicles
{
    public Vehicle CreateVehicle(string color, int numDoors, string[] options = string[0])
    {
        Civic vehicle = new Civic();
        vehicle.Doors = numDoors;
        vehicle.Color = color;
        vehicle.Options = options;
        return vehicle;
    }

    public bool CanCreateModel(string model) 
    {
        return model.Equals("Civic");
    }
}

We do the same thing for all of our other models. The conditional evaluator is moved from the if block into the CanCreateModel method, and the body of the if statement is moved into CreateVehicle. All that’s left is to apply the same trick that we did in the VehicleFactory to auto-discover our model factories. But - how do we make sure the Honda Factory only discovers factories relating to it, rather than all makes? The simplest approach here is to refactor IMakeVehicles to be a generic interface:

public interface IMakeVehicles<TModelType> where TModelType : Vehicle
{
    TModelType CreateVehicle(string color, int numDoors, string[] options = string[0]);
    bool CanCreateModel(string model);
}

We then update our CivicFactory to use the following definition:

public class CivicFactory : IMakeVehicles<Honda>
{
    public Honda CreateVehicle(...);
}

This allows us to use the same Assembly trick from earlier on a strongly-typed interface. The assumption here is that Civic : Honda and Honda : Vehicle. Without that relationship, this trick will not work.

We finish up by changing our HondaFactory to look like the following:

public class HondaFactory : IVehicleFactory
{
    private IList<IMakeVehicles<Honda>> factories = new List<IMakeVehicles<Honda>>();

    public VehicleFactory()
    {
        var discoveredFactories = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => type.GetInterfaces().Any(intrfce => intrfce.Equals(typeof(IVehicleFactory<Honda>))));
        this.factories.AddRange(discoveredFactories);
    }

    public Vehicle buildVehicle(string model, string color, int numDoors, string[] options = string[0])
    {
        IVehicleFactory usableFactory = this.factories.SingleOrDefault(fac => fac.CanCreateModel(model));
        if (usableFactory == null) throw new InvalidArgumentException("Cannot create vehicle of the requested model.");
        return usableFactory.CreateVehicle(color, numDoors, options);
    }

    public bool CanCreateMake(string make) 
    {
        return make.Equals("Honda");
    }
}

Summary

As we have seen, you can easily refactor your logic in such a way that’s a lot more straight-forward to maintain. You’ve created a factory that can be reused in multiple places across your application. Instead of having to maintain a gargantuan and growing class, you being to worry about micro-factories that are focused on your specific needs. Your micro-factories are able to be reused across components. You have encapsulated all logic associated with the creation of a particular vehicle in a single class, rather than splitting the determination of when to create the class and how to create it in separate classes. Your unit test classes for each of these components are so small, you stop worrying about how to create your 18-level chain mock to test a single conditional statement. In short: you achieve OO bliss.

What I’ve introduced is not new. In fact, this is a variation of the Command Pattern. I also heavily rely on this pattern when I’m implementing strategies within my code. Strategies change often, so when I need to introduce a new strategy, I create the class, implement the interface, rebuild, and go! Removing a strategy is as simple as pressing the delete key. One class and everything else is taken care of for me.

If you have questions or comments about this pattern, please feel free to reach out to me via e-mail or through the comment page below. I would love to hear other’s ideas on this implementation.