How to Get Rid of Annoying IFs Forever
Tags: oop, programming, codenewbie, tutorial
Published: 2020-11-09 03:30:15

*Why the first instruction we learn to program should be the last to use.*

Nobody uses [GOTO instruction](https://en.wikipedia.org/wiki/Goto) anymore and few programming languages still support it.

We have matured and confirmed [spaghetti code](https://en.wikipedia.org/wiki/Spaghetti_code) is unmaintainable and error prone.
[Structured Programming](https://en.wikipedia.org/wiki/Structured_programming) solved that problem years ago.

We got rid of the sentence thanks to [Edsger Dijkstra's](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra) incredible [paper](https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf): Go To Statement Considered [Harmful](https://en.wikipedia.org/wiki/Goto#Criticism).

Next evolution step will be removing most [IF statements](https://en.wikipedia.org/wiki/Conditional_%28computer_programming%29). 

**IFs** / **Cases** and **Switches** are *GOTOs* disguised as structured flow. 

Our tool will be Object Oriented Programming principles.

![forest-road-5228673_1920.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1604271082172/KS_g__hhm.jpeg)

Photo <a href="https://pixabay.com/es/users/przemko-1606435/">Przemysław Bromberek</a> en <a href="https://pixabay.com/">Pixabay</a>
 
 # The Problem

Most **IF** sentences are [coupled](https://mcsee.hashnode.dev/coupling-the-one-and-only-software-design-problem) to *accidental decisions*. This coupling generates ripple effect and make code harder to maintain.

IFs are considered as [Harmful](https://sddconf.com/brands/sdd/library/If_considered_Harmful_-_Jules_May_-_SDD2017.pdf) as **GOTOs**.

{% youtube z43bmaMwagI %}
 
*IF sentences* violate [open/closed principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). Our designs will be less extensible and closed to extension.

What is more, **IFs** are open doors to even worse problems, like **switches**, **cases**, **defaults**, **return**, **continue** and **breaks**.

They make our algorithms darker and force us to build *[accidentally](https://mcsee.hashnode.dev/no-silver-bullet) complex* solutions. 

People out of software development cannot explain why we use this branching sentence. This is a code smell.

# Solutions

Before we move on and remove IF sentences we should decide if its an *essential* one or an *accidental* If.

To check this out we will look for answers in real world through bijection.

## Essential Ifs

Let's see an *essential* **IF** statement

{% gist https://gist.github.com/mcsee/ab050467c32205a1b8623352f6bb8dd8 %}

We should decide whether to remove this **if sentence** or not.

We must understand whether it represents a business rule (*essential*) or an implementation artifact (*accidental*).

In the case above we will honor our [bijection](https://mcsee.hashnode.dev/the-one-and-only-software-design-principle). So we will NOT replace the if.

> People In real world describe age constraints in natural language using **IFs**

## Accidental Ifs

Let us dive now into *bad* **IFs**.

{% gist https://gist.github.com/mcsee/3c0d6d285a537041900a92f71c9c73c8 %}

The movie rating **IF** is not related to a **real world If** but to accidental (and coupled) implementation.

Our design decision was to model *ratings* with *strings*.

This is a classic *neither open to extension, nor closed to modification* solution.

Let's see what happens with new requirements.

{% gist https://gist.github.com/mcsee/c237bf02c4ec561939b7ec5973fc913a %}

We can detect some *Code Smells*:

1. Code is polluted with IFs.
2. A default statement is missing.
3. New ratings will bring new IFs.
4. The strings representing ratings are not first class objects. A typo will introduce hard to find errors.
5. We are forced to add getters on *Movies* to take decisions.

# The Recipe

Let's fix this mess with these steps:

> 1. Create a Polymorphic Hierarchy for every IF condition (if it doesn't already exist).
> 2. Move every *IF Body* to the former abstraction .
> 3. Replace IF Call by polymorphic method call.

On our example:

{% gist https://gist.github.com/mcsee/747c2a4a6bb3531966b7e98ad92924e3 %}

With this outcome:

1- Code is polluted with **IFs**.

*We should add no more __IFs__. Extending the model will be enough.*

2- A default statement is missing.

*In this case default behaviour is no needed since exceptions break flow. In many times a Null Object will be enough.*

{% post https://dev.to/mcsee/code-smell-12-null-la4 %}

3- New ratings will bring new IFs.

*We will address it with polymorphic new instances.*

4- The strings representing ratings are not first class objects. A typo will introduce hard to find errors.

*This is hidden in Ratings implementation.*

5- We are forced to add getters on *Movies* to take decisions.

*We will clear this problem favoring [Demeter's Law](https://en.wikipedia.org/wiki/Law_of_Demeter).*

{% post https://dev.to/mcsee/code-smell-08-long-chains-of-collaborations-30c3 %}

* * *

## Breaking this collaborator chain

{% gist https://gist.github.com/mcsee/49382a7cff708d0b4a8c2e602295484f %}

{% gist https://gist.github.com/mcsee/c41292108e66a2db187151c10f690230 %}

Rating is private so we don't break encapsulation.

As a consequence we are safe to avoid *[getters](https://mcsee.hashnode.dev/nude-models-part-ii-getters)*.

## Applying the recipe to all IF conditions

Now we have the secret formula we can go further and try to remove the *essential* **IF condition** related to age.

{% gist https://gist.github.com/mcsee/884f18fcdf4e1ada78a4ad095abb5c1e %}

We replaced all **IFs**. In the later case using [Double Dispatch Technique](https://en.wikipedia.org/wiki/Double_dispatch)

We used our formula and it worked. But there's a smell of **over design**.

1. Classes representing Ages are not related to real concepts on our [model](https://mcsee.hashnode.dev/what-is-wrong-with-software).
2. Model is too complex.
3. We will need new classes related to new age groups.
4. Age groups might not be disjoint.

We should avoid the last design and set a **clear boundary** between *essential* and *accidental* ifs.

>> A Good design rule is to create abstractions if they belong to the same domain (movies and ratings) and don't do it if they cross domains (movies and ages).

## Do Ifs stink?

According to evidence shown above. We should consider many *IFs* to be a code smell and [tackle them](https://sourcemaking.com/refactoring/replace-conditional-with-polymorphism) with our recipe.
 
## Why this is happening?

This article (and many others) recommend avoiding most **IF sentences**. 
This will be very hard for all developers very comfortable with its usage.

Remember, Laziness and hidden assumptions are very rooted on our profession. We have been (ab)using **IFs** for decades and our software is not the best version of it. 

This is [a root cause analysis](https://blog.codecentric.de/en/2014/02/curly-braces/) of a serious SSL defect on IOS caused by a lazy case:

This article's thesis suggests there's a correlation between **IFs/Switch/Case** and defects.

You should give a try and avoid **IF conditionals.**

# Conclusions

With this simple technique we will be able to remove, in a procedural way, all accidental ifs.

This will make our models less coupled and more extensive.

Null object pattern is a special case of this technique. We will be able to remove all NULLs since:

> NULL ifs are always [accidental](https://mcsee.hashnode.dev/null-the-billion-dollar-mistake]).

# Credits

We have been using *If removal technique* at [Universidad de Buenos Aires](https://exactas.uba.ar/) for several years. Kudos to all my fellow teachers for all the experience we gathered together with it. 
 
![Egm99IwXYAI8S-G.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1604269685142/ID_Gu56EQ.jpeg)