avatarNicklas Millard

Summary

The article discusses alternative coding strategies to replace the overused and often problematic If-Else statements for improved software design and maintainability.

Abstract

The article "Better Software Without If-Else" by Nicklas Millard emphasizes the drawbacks of excessive reliance on If-Else statements, which can lead to complicated code, reduced readability, and challenging refactoring. Millard, a software development engineer in a rapidly growing bank, advocates for the adoption of more sophisticated branching strategies. He presents five techniques to eliminate unnecessary If-Else blocks, ranging from simple value assignments to advanced design patterns like the strategy pattern. These techniques include simplifying code by removing redundant else blocks, using switch statements or ternary operators for value assignments, implementing guard clauses for precondition checking, replacing If-Else with dictionaries for extensible applications, and adhering to SOLID principles by employing the strategy pattern for dynamic type discovery and application extension. The article aims to guide developers from junior to senior levels in writing cleaner, more maintainable, and scalable code.

Opinions

  • If-Else statements are often misused, leading to suboptimal code design and maintainability issues.
  • Developers should progress beyond If-Else as the default solution for code branching.
  • Simplifying code by removing unnecessary else blocks can significantly improve code readability.
  • The use of switch statements, ternary operators, and guard clauses can enhance code clarity and prevent invalid method executions.
  • Dictionaries can replace If-Else chains for more maintainable and scalable code, especially when anticipating future extensions.
  • Advanced design patterns like the strategy pattern align with SOLID principles and facilitate dynamic application extension without violating the Open/Closed principle.
  • The article suggests that the ability to choose appropriate branching strategies is a mark of a senior developer.
  • Millard encourages developers to adopt object-oriented principles and refactoring techniques to improve code quality and extensibility.

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 distinguish juniors from seniors.

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

The difficulty will increase with each example.

Quick side-note: if you’re a visual learner, you can follow this new video that explains different approaches to eliminating if-else and switch cases.

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.

🔔 Want more articles like this? Sign up here.

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:

  1. Extract each branch into separate strategy classes with a common interface
  2. Dynamically find all classes implementing the common interface
  3. 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.

Ideally, the type discovery and dictionary would happen outside of the PrintOrder method. But anyway, let’s just quickly walk thru the code.

The method signature has been kept the same, as the caller wouldn’t need to know about our refactoring.

First, get all types in the assembly that implements the common interface IOrderOutputStrategy. Then, we build a dictionary, with the name of the formatters’ displayName is key, and the type is the value.

Then the formatter type is chosen from the dictionary, and we try to instantiate a strategy object.

Lastly, the strategy object’s ConvertOrderToString is invoked.

Resources for the curious
--------------------------
How to make your code more Object-Oriented by Zoran Horvat
Replace Conditional Logic with Strategy Pattern by JetBrains
How To Replace Many if Statements in Java by baeldung
Replace if/else in C# by kodify.net

Nicklas Millard is a software development engineer in one of the fastest-growing banks, building mission-critical financial services infrastructure.

Previously, he was a Big4 Senior Tech Consultant developing software for commercial clients and government institutions.

New YouTube Channel (@Nicklas Millard)

Connect on LinkedIn

Technology
Programming
Software Development
Software Engineering
Best Practices
Recommended from ReadMedium