A Factory - Sans Conditionals
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:
- Won't you create too many classes to maintain? I have a hard enough time navigating my codebase as-is.
- Aren't classes "heavy"? Won't that bloat the application?
- How do I know what my application is doing without if/else if/else statements?
My responses are usually:
- 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.
- 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.
- 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.
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:
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.
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:
And here’s our ChevroletFactory
:
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:
CanCreateMake
evaluates the make and returns whether this factory can handle it. We then are able to change our HondaFactory
to look like:
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:
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.
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:
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:
So, let’s create our first vehicle, a 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:
We then update our CivicFactory
to use the following definition:
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:
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.