Refactoring conditions – Remove Control Flag remove control flag four

Refactoring condition-Remove Control Flag removes control flag 4

1. Remove control tags

1.1. Usage scenarios

In a sequence of Boolean expressions, a variable acts as a “control flag”. Replace control tags with break statements or return statements.

In a series of conditional expressions, you will often see control flags to determine when to stop checking the condition

// conditional tag
 set done to false
 while not done
   if (condition)
       do something
       set done to true
   next step of loop

The inconvenience of such control flags outweighs the convenience.
The reason why people use such control tags is because the principles of structured programming tell them that each subroutine can only have one entry and one exit. I subscribe to the “single entry” principle (and modern programming languages force us to do so), but the “single exit” principle will let you add nasty control tags to your code, greatly reducing the readability of conditional expressions.
This is why programming languages provide break and continue statements: use them to break out of complex conditional statements. The effect of removing the control flags will often surprise you: the real purpose of the conditional statement will be much clearer.

1.2. How to do it

  • The most obvious way to deal with control tags is to use the break statement or continue statement provided by Java.
  • Find the control tag value that lets you jump out of this logic.
  • Find the statement that assigns a value to the marked variable, and replace it with an appropriate break statement or continue statement.
  • After each replacement, compile and test.
  • In programming languages that do not provide break and continue statements, the following methods can be used.
  • Use Extract Method (110) to extract the entire logic into a single function.
  • Find the control tag value that lets you jump out of this logic.
  • Find the statement that assigns a value to the marked variable and replace it with an appropriate return statement.
  • After each replacement, compile and test.
  • Even in programming languages that support break and continue statements, I usually prefer the second option above. Because the return statement can be very clear: no other code in the function will be executed. If there is still this type of code, you will need to extract this code sooner or later.
  • Please pay attention to whether the tag variable will affect the final result of this logic. If it matters, the value of the control flag must be preserved after using the break statement. If you have extracted this logic into a separate function, you can also return the control tag value in the return statement.

1.3. Example

1. Replace simple control tags with break

The following function checks if a list of names contains the names of two suspects (these names are hardcoded in the code)

 void checkSecurity(String[] people) {<!-- -->
  // conditional tag
      boolean found = false;
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (! found) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
               found = true;
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               found = true;
             }
          }
      }
  }

In this case it is easy to find the control flag: when the variable found is assigned true, the search ends. I can introduce break statements one by one

 void checkSecurity(String[] people) {<!-- -->
      boolean found = false;
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (! found) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
             // introduce break
             break;
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               found = true;
             }
          }
      }
  }

Until all statements that assign values to the found variable are replaced

 void checkSecurity(String[] people) {<!-- -->
      boolean found = false;
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (! found) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
               // introduce break
               break;
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               // introduce break
               break;
             }
          }
      }
  }

Then I can remove all references to the control tag

void checkSecurity(String[] people) {<!-- -->
    for (int i = 0; i < people. length; i ++ ) {<!-- -->
        if (people[i].equals ("Don")){<!-- -->
           sendAlert();
           break;
        }
        if (people[i].equals ("John")){<!-- -->
           sendAlert();
           break;
        }
    }
}

2. Return the control tag with return

Another form of this refactoring would use a return statement. To illustrate this usage, I’ve slightly modified the previous example to control markup record search results:

 void checkSecurity(String[] people) {<!-- -->
  // tag variable
      String found = "";
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (found.equals("")) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
               found = "Don";
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               found = "John";
             }
          }
      }
      // found value as parameter
      someLaterCode(found);
  }

Here, the variable found does two things: it is both a control flag and the result of an operation. In this case, I like to first distill the code that calculates the found variable into a separate function:

 void checkSecurity(String[] people) {<!-- -->
      String found = foundMiscreant(people);
      someLaterCode(found);
  }
  // Extract the calculated found value to a function
  String foundMiscreant(String[] people){<!-- -->
      String found = "";
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (found.equals("")) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
               found = "Don";
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               found = "John";
             }
          }
      }
      return found;
  }

Then replace the control tag with a return statement

 String foundMiscreant(String[] people){<!-- -->
      String found = "";
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (found.equals("")) {<!-- -->
             if (people[i].equals ("Don")){<!-- -->
               sendAlert();
              return "Don";
             }
             if (people[i].equals ("John")){<!-- -->
               sendAlert();
               found = "John";
             }
          }
      }
      return found;
  }

Finally remove the control tag completely

 String foundMiscreant(String[] people){<!-- -->
      for (int i = 0; i < people. length; i ++ ) {<!-- -->
          if (people[i].equals ("Don")){<!-- -->
             sendAlert();
             return "Don";
          }
          if (people[i].equals ("John")){<!-- -->
             sendAlert();
             return "John";
          }
      }
      return "";
  }

Even if you don’t need to return a value, you can use a return statement instead of a control tag. At this time, you only need an empty return statement.

Of course, if you deal with functions with side effects in this way, there will be some problems. So I need to separate the side effects of the function with Separate Query from Modifier (279). You’ll see examples of this later.