Part 2: Stop Using If-Else Statements
By: Nicklas Millard
Tags: Technology, Software Engineering, Csharp, Programming, Software Development

APPLIED DESIGN PATTERNS: STRATEGY

Part 2: Stop Using If-Else Statements

Let’s have yet another look at how you can replace if-else statements.

Okay, we both already agree using If-Else statements everywhere is an awful practice.

You’ve without a shred of doubt met If-Else statements that made your head ache six ways from Sunday. Nasty branching and unclear responsibilities. We might as well slap some goto in there while we’re at it just for sh*ts and giggles.

Instructors and teachers love If-Else. It’s their hammer and everything’s a nail. Gotta decide which logic to execute? Use If-Else. Want to create a factory? Use If-Else. You get the point already…

We’ll be refactoring this illustrative hot piece of mess below to something extensible and production ready.

Terrible to look at. And, yes, it could have been implemented with switch as well. This kind of code is nevertheless prevalent.

You know it’s bad. But it’s fixable. A bit of refactoring and we’re back to highly extensible and maintainable code that’ll make you sleep like a baby.

“So, how do we replace these pieces of pain inducing If-Else statements?”

With strategy objects and type discovery.

You’ve likely already heard of the strategy pattern, but might still secretly wonder what the fuzz is all about. Here’s a brief introduction to a pattern that’ll change how you write branching in the future.

We often need to determine which logic to execute based on some condition. By creating a group of classes with a common interface, in combination with type discovery, we can easily swap which logic to execute, without the need for If-Else.

Nice, huh? We just call a specialized object’s method instead of extending our application with a nightmare of endless If-Else branching statements.

We’ll be refactoring the code above, ensuring we adhere to the SOLID principles. Especially with the Open/Closed principle in mind.

Demo time

1 First we start by extracting the the logic out of the horrible If-Else statement, and place it in separate strategy classes. At the same time, we create a common interface.

Each strategy class implements the common interface. Also, I’ve applied an attribute on the class, which provides us the opportunity to give the strategy a friendly name. The attribute class is define later in this article.

2 Then, we create a method in the Order class which takes the strategy interface as a parameter. Here’s a snippet of the entire Order class.

This allows us to delegate the logic to the specialized class, instead of writing horrible, not-easy-to-extend if-else statements.

3 Now the part where we actually remove the If-Else hell from the illustrative example at the beginning.

This provide some real extensibility to our application.

Let’s briefly walk thru the type discovery process.

We’re building a dictionary containing all the types that implement the common interface, and use the name from the attribute as the key.

Then, we let the user enter some text into the console that will match one of the output formatters name.

Based on the input, we first find the correspond type in the dictionary and create an instance of that type.

The instance is passed to the Order’s GenerateOutput method.

It’s more code, no doubt. But it will allow us to dynamically discover new formatter strategies as they are added to the solution. Something If-Else won’t provide you with, no matter how hard instructors try to push it.

One more thing…

If you wonder about the [OutputFormatterName("")] , above the strategy classes, the implementation looks like this below.

A very simple attribute class that provides us with a friendly display name.

“What to do if I need a new way to format the output?

You create a new class that implements the IOrderOutputStrategy . It’s honestly that simple. The type discovery process will take care of “registering” the new formatter with the application.

By defining the GenerateOuput method on the Order, we don’t need to branch our code using If-Else. We just delegate the responsibility to the specialized class.

“Again, you’re creating a lot of classes to do something simple!”

Sure, it’s a lot of additional classes. But they are insanely simple.

They have meaningful names derived from the functional requirements. Other developers would recognize their purpose from the get-go.

I can also walk thru logic with business people and they’re completely onboard with what I’m talking about with only a bit of handholding — it’s code after all.

Should we really limit our expressiveness, just to accommodate people who are stuck with If-Else?

“But won’t there be situations when If-Else is okay?”

Sure. Sometimes… If you’re into competitive programming, writing something that needs to be highly optimized, if you know something will absolutely not change (until it does)— or doing a college assignment. Instructors love that sh*t.