This article does not confirm or deny which method of writing, just to provide you with some other coding ideas or some ideas worth learning.
Designing better software, five ways to replace if-else, from getting started to advanced examples
If-else is usually a bad choice, resulting in complex design, poor code readability, and possibly difficult refactoring.
However, if-else has become the de facto code branching solution, and it does make sense. This is the first thing taught to all aspiring developers.
Unfortunately, many developers never move on to a more appropriate branching strategy. Some people’s catchphrase is: if-else is a hammer, everything is a nail.
I’m going to show you some tricks and patterns that will put an end to this horrible practice. Each example becomes more difficult.
Completely unnecessary Else blocks
This is perhaps one of the biggest SINS of junior developers. The following example is a good example of what happens when you are deemed if-else awesome:
Simple if-else
Simply remove the else ‘block to simplify the process, as shown below:
Removed else
Looks more professional, right? You’ll see that you don’t really need another block at all. As in this case, you want to do something and return immediately if certain conditions are met.
Value distribution
If you want to assign new values to variables based on some input provided, stop the if-else nonsense, a more readable method.
Value assignment with if-else
Simple as it is, it is terrible. First, the if-else is easily replaced by the switch here. However, we can further simplify this code by removing the else completely.
If statements with fast return
If we don’t use else, we’ll be left with clean, readable code. Note that I have also changed the style to quick return rather than single return statement. There is no point in continuing to test a value if the correct value has already been found.
Prerequisites Check
In general, I find that it makes no sense to continue if the method provides an invalid value. Suppose we had a DefineGender method from the past that required that the input values provided must always be 0 or 1.
Method without value checks
It makes no sense to execute this method without validation of value. Therefore, we need to check some prerequisites before allowing the method to continue.
Using the protection clause defensive coding technique, you will examine the input value of the method and then proceed with the method execution.
Check preconditions with guard clauses
So far, we have made sure that the master logic is executed only if the value falls within the expected range. Now, IF has also been replaced by a ternary, because it is no longer necessary to return “unknown” at the end by default.
Convert if-else to dictionary, avoiding if-else altogether
Let’s say you need to perform some actions that are selected based on certain conditions, and we know we’ll have to add more actions later.
One might prefer to use the time-tested if-else. If you add a new action, you simply add something else. Simple but, in terms of maintenance, this approach is not a good design.
Knowing that we’ll need to add new operations later, we can refactor the if-else into a dictionary.
Readability has greatly improved, and the code can be inferred more easily. Note that the dictionary is placed inside the method for illustrative purposes only. You might want to provide it from somewhere else.
Extend the application to avoid if-else altogether
This is a slightly more advanced example. By replacing them with objects, you know when to eliminate ifs altogether.
Often, you’ll find yourself having to extend parts of your application. As a junior developer, you might be tempted to do this by adding additional if-else (or else-if) statements.
Take this illustrative example. In this case, we need to display the Order instance as a string. First, we only have two string representations: JSON and plain text.
Using an if-else at this stage isn’t a big deal, If we can easily replace it with something Else, as mentioned above.
Knowing that we need to extend this part of the application, this approach is absolutely unacceptable.
Not only does the above code violate the “on/off” principle, it doesn’t read well, and it causes maintainability problems.
The right approach is the SOLID principles approach, which we do by implementing a dynamic type discovery process (in this case, the policy pattern).
The process of refactoring this messy process is as follows:
- Each branch is extracted into a separate policy class using a public interface.
- Dynamically find all classes that implement a common interface.
- The input determines which policy to execute.
The code to replace the above example is shown below. Yes, that’s the way more code works. It requires that you understand how type discovery works. But dynamically extending applications is an advanced topic.
I show only the exact part of the if-else example that will be replaced. If you want to see all the objects involved, check out this point.
Let’s take a quick look at the code. The method signature remains unchanged because the caller does not need to know about our refactoring.
First, get all the types in the assembly that implement the generic interface IOrderOutputStrategy. Then we create a dictionary and formatter’s displayName has the name key and type value.
Then select the formatter type from the dictionary and try to instantiate the policy object. Finally, the ConvertOrderToString of the policy object is called.