Clean Code Applied to JavaScript — Part VI. Avoid conditionality

Complex conditional judgments can make code difficult to understand, thus increasing maintenance costs. In addition, the complexity of the conditions is often an indicator of how coupled the code is. If you want to improve the quality of your code, it’s best to avoid writing code with complex conditional decisions.

In this article, I’ll introduce some methods and suggestions for avoiding complex conditions in your code. In the specific examples below, we’ll use the JavaScript/TypeScript language as an example, but the concepts discussed in this article can be applied to any programming language, since the suggestions and methodology tips we offer are not limited to a particular programming language.

Do not use flags as function arguments

The first piece of advice to avoid complex conditions is not to take markers as function arguments, which would break the functionality of the function. Instead of using one function to implement two logical functions, we have to create two functions to implement each logical function because they are different functions.

In the first example, the isPremium parameter is used to determine which function to use. However, the correct approach is to declare two different functions to implement both functions.

// Dirty
function book(customer, isPremium) {
  // ...
  if (isPremium) {
    premiumLogic();
  } else{ regularLogic(); }}Copy the code
// Clean (Declarative way)
function bookPremium (customer) {
  premiumLogic();
}

function bookRegular (customer) {
  retularLogic();
}
Copy the code

Encapsulation judgment condition

Wrap the condition in a function with clear semantics so that you can understand the code logic more intuitively. In the first example, you need to think about the conditional statement to understand it, while in the second example you can understand it directly from the name of the function.

if (platform.state === 'fetching' && isEmpty(cart)) {
    // ...
}  
Copy the code
function showLoading(platform, cart) {
    return platform.state === 'fetching' && isEmpty(cart);
}

if (showLoading(platform, cart)) {
    // ...
}
Copy the code

Replace nested conditional statements with Guard Clauses

This advice is critical for programmers in development, where there should be no nested conditional statements. Defense statements are one of our main techniques for avoiding nested conditions and can be implemented perfectly without the need for the else keyword.

The following example demonstrates the health statements in the code, which has been greatly improved in readability through automatic formatting by the editor.

If you want to learn more about Guard Clauses, I recommend you read my detailed article: Guard Clauses.

function getPayAmount() {
    let result;
    if (isDead){
        result = deadAmount();
    } else {
        if (isSeparated){
            result = separatedAmount();
        } else {
            if (isRetired){
                result = retiredAmount();
            }else{ result = normalPayAmount(); }}}return result;
}
Copy the code
function getPayAmount() {
    if (isDead) return deadAmount();
    if (isSeparated) return separatedAmount();
    if (isRetired) return retiredAmount();
    return normalPayAmount();
}
Copy the code

The Null Object pattern

Another common error you’ll see in junior programmers’ code is constantly checking for null objects and using that check to determine whether the default action is displayed. This pattern is called the empty object pattern.

In the following example, each object in the array is checked and the sound method is not executed if Animal is NULL.

On the other hand, if we create an object that encapsulates the behavior of an empty object, we no longer need to perform the validation shown in the first piece of code.

class Dog {
  sound() {
    return 'bark'; }} ['dog'.null].map((animal) = > {
  if(animal ! = =null) { sound(); }});Copy the code
class Dog {
  sound() {
    return 'bark'; }}class NullAnimal {
  sound() {
    return null; }}function getAnimal(type) {
  return type === 'dog' ? new Dog() : new NullAnimal();
}

['dog'.null].map((animal) = > getAnimal(animal).sound());
// Returns ["bark", null]
Copy the code

If you want to learn more about this Pattern, I recommend reading my specific article: Null-Object Pattern

Use polymorphic deletion conditions

Most programmers would consider the switch control statement to be more concise than the if statement, although the use of the two is somewhat different. If we use a switch in our code, it will actually increase the complexity of our code, and we will end up thinking too much.

In the following example, the method of an object is defined by its type, but the conditional statement is overused. In this scenario, we can use class inheritance to create a class for each specific type, using polymorphism to avoid using conditional judgment.

function Auto() {
}
Auto.prototype.getProperty = function () {
    switch (type) {
        case BIKE:
            return getBaseProperty();
        case CAR:
            return getBaseProperty() - getLoadFactor();
        case BUS:
            return (isNailed) ? 
            0 : 
            getBaseProperty(voltage);
    }
    throw new Exception("Should be unreachable");
};
Copy the code
abstract class Auto { 
    abstract getProperty();
}

class Bike extends Auto {
    getProperty() {
        returngetBaseProperty(); }}class Car extends Auto {
    getProperty() {
        returngetBaseProperty() - getLoadFactor(); }}class Bus extends Auto {
    getProperty() {
        return (isNailed) ? 
                0: getBaseProperty(voltage); }}// Somewhere in client code
speed = auto.getProperty();
Copy the code

Remove conditions in policy/command mode

We can also use policy or command patterns in our code to avoid complex conditions.

If you want to learn more about these two patterns, I recommend that you read the specific articles I delving into these patterns: Strategy Pattern and Command Pattern.

In the specific examples in this section, you can see the policy pattern for dynamically selecting policies. Notice how different strategies are used to address the complexity of switch constructs.

function logMessage(message = "CRITICAL::The system ..."){
    const parts = message.split("... ""); 
    const level = parts[0];

    switch (level) {
        case 'NOTICE':
            console.log("Notice")
            break;
        case 'CRITICAL':
            console.log("Critical");
            break;
        case 'CATASTROPHE':
           console.log("Castastrophe");
            break; }}Copy the code
const strategies = {
    criticalStrategy,
    noticeStrategy,
    catastropheStrategy,
}
function logMessage(message = "CRITICAL::The system ...") {
    const [level, messageLog] = message.split("... "");
    const strategy = `${level.toLowerCase()}Strategy`;
    const output = strategies[strategy](messageLog);
}
function criticalStrategy(param) {
    console.log("Critical: " + param);
}
function noticeStrategy(param) {
    console.log("Notice: " + param);
}
function catastropheStrategy(param) {
    console.log("Catastrophe: " + param);
}
logMessage();
logMessage("CATASTROPHE:: A big Catastrophe");
Copy the code

conclusion

In this paper, we propose some suggestions for avoiding complex conditional judgments. Complex conditional judgments can make code less readable and highly coupled, resulting in less flexibility. These methods and suggestions allow us to avoid complex judgments in our code and take our code quality to the next level.

Finally, we conclude the following points:

  • Do not use flags as function arguments
  • Encapsulation judgment condition
  • Replace nested conditional statements with Guard Clauses
  • The Null Object pattern
  • Use polymorphisms to avoid conditional judgment
  • Use policy patterns to avoid conditional judgments
  • Use command mode to avoid conditional judgment

Related articles

  • How to clean JavaScript code – Comments
  • How to clean JavaScript code – Function section
  • Simple practice for writing clean React code
  • Simple tips for writing a concise React component

Click to follow the official account “KooFE Front-end Team”