Better Software Without If-Else
By: Nicklas Millard
Tags: Best Practices, Software Development, Technology, Software Engineering, Programming

DESIGNING BETTER SOFTWARE

Better Software Without If-Else

5 Ways to Replace If-Else. Beginner to advanced examples

Let me just say this right off the bat: If-Else is often a poor choice.

It leads to complicated designs, less readable code, and may be pain inducing to refactor.

Nevertheless, If-Else has become the de facto solution for code branching — which does make sense. It’s one of the first things any aspiring developer is taught. Unfortunately, lots of developers never advance to more suitable branching strategies.

Some live by the mantra: If-Else is a hammer and everything’s a nail.

The inability to determine when to use a more suitable approach is among those that distinguishes juniors from seniors.

I’ll show you some techniques and patterns that’ll put an end to this horrific practice.

The difficulty will increase by each example.

1 Entirely unnecessary else blocks

This is perhaps one of those junior developers are most guilty of. The example below is a prime illustration of what happens when you get beaten into thinking If-Else is great.

Simple if-else

It can be simplified by just removing the else ` block.

Removed else

More professional looking, right?

You’ll regularly find there’s really no need for an else block. Like in this case, you want to do something if a certain condition is met and return immediately.

2 Value assignment

If you want to assign a new value to a variable based on some provided input, then stop the If-Else nonsense — there’s a more readable approach.

Value assignment with if-else

Despite the simplicity, it’s awful. First off, If-Else is easily replaced with a switch here. But, we can simplify this code even further by removing else if and else altogether.

If statements with fast return

Take away the else if and else , and we are left with clean, readable code. Notice that I’ve also changed the style to be fast return opposed to single return statement — it simply doesn’t make sense to continue testing a value if the correct one has already been found.

3 Precondition checking

Most often, I find that it won’t make sense to continue executing a method if it’s provided with invalid values.

Say we have the DetermineGender method from before, with the requirement that the provided input value must always be 0 or 1.

Method without value checks

Executing the method without value validation doesn’t make any sense. So, we’ll need to check some preconditions before we allow the method continuing its executing.

Applying the guard clause defensive coding technique, you’ll check method input values and only move on to executing the method if.

Check preconditions with guard clauses

At this point, we’ve made sure the main logic is only executed if the value falls within the expected range.

The IFs have also been replaced with ternary now that it doesn’t make sense to have a default return of “Unknown” at the end any longer.

4 If-Else to Dictionary — avoid If-Else entirely

Say you need to perform some operation that’ll be selected based on some condition, and we know we’ll have to add more operations later.

One is perhaps inclined to use the tried and true, If-Else. Adding a new operation is simply a matter of slapping in an extra else if. That’s simple. This approach is however not a great design in terms of maintenance.

Knowing we need to add new operations later, we can refactor the If-Else to a dictionary.

Readability has vastly increased and it’s easier to reason about this code.

Note that, the dictionary is only placed inside the method for illustrative purposes. You’d likely want it to be provided from somewhere else.

5 Extending applications — avoid If-Else entirely

This is a slightly more advanced example.

Let me also clarify something real quick… This is a more “enterprisy” approach. It won’t be your typical “lemme just replace that if-else” scenario. Now, read on.

Know when to even eliminate Ifs entirely, by replacing them with objects.

Often, you’ll find yourself having to extend some part of an application. As a junior developer, you may be inclined to do so by just adding an extra If-Else (i.e. else-if) statement.

Take this illustrative example. Here, we need to present an Order instance as a string. First, we only have two kinds of string representation, JSON and plain text. Using If-Else at this stage is not a big issue, tho we can easily replace else if with just if as demonstrated earlier.

Knowing we need to extend this part of the application, this approach is definitely not acceptable.

Not only does the code above violate the Open/Closed principle, it doesn’t read well and will cause maintainability headaches.

The correct approach is one that adheres to the SOLID principles — and we do this by implementing a dynamic type discovery process, and in this case, the strategy pattern.

The process to refactor this hot piece of mess, is as following:

Extract each branch into separate strategy classes with a common interface Dynamically find all classes implementing the common interface Decide which strategy to execute based on input

The code that’ll replace the example above looks like this. And yes, it’s way more code. It requires you to know how type discovery works. But dynamically extending an application is an advanced topic.

I’m only showing the exact part that’ll replace the If-Else example. Take a look at this gist if you want to see all objects involved.