Learning reconstruction (5) – simplifying conditional expression

Time:2020-2-11

1. Decompose conditional
Scenario: you have a complex if then else statement. The independent functions are extracted from if, then and else.
Example:
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
  charge = quantity * mWinterRate + mWinterServiceCharge;
} else {
  charge = quantity * mSummerRate;
}
Refactoring as follows:
if (notSummer(date)) {
  charge = winterCharge(quantity);
} else {
  charge = summerCharge(quantity);
}
private boolean notSummer(Date date) {
  return date.before(SUMMER_START) || date.after(SUMMER_END);
}
private double winterCharge(int quantity) {
  return quantity * mWinterRate + mWinterServiceCharge;
}
private double summerCharge(int quantity) {
  return quantity * mSummerRate;
}

2. Consolidate conditional expression
Application scenario: you have a series of conditional tests that all get the same results. Combine these tests into a conditional expression and refine the conditional expression into an independent function.
Example:
double disabilityAmount() {
if(mSeniority < 2) {
return 0;
}
if(mMonthsDisabled > 12) {
return 0;
}
if(mIsPartTime) {
return 0;
}
// compute the disability amount …
}
Refactoring as follows:
double disabilityAmount() {
  if(isNotEligibleForDisability()) {
    return 0;
  }
  // compute the disability amount …
}
private boolean isNotEligibleForDisability() {
  return (mSeniority < 2) || (mMonthsDisabled > 12) || mIsPartTime;
}

3. Consolidate duplicate conditional fragments
Application scenario: there is the same piece of code on each branch of the conditional expression. Move the duplicate code out of the conditional expression.
Example:
if(isSpecialDeal()) {
  total = price * 0.95; send();
} else {
  total = price * 0.98; send();
}
Refactoring as follows:
if(isSpecialDeal()) {
  total = price * 0.95;
} else {
  total = price * 0.98;
}
send();

4. Remove control flag
Application scenario: in a series of Boolean expressions, a variable has the function of “control mark”. Replace the control tag with a break or return statement.
Example: void checksecurity (string [] people){
  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;
} } } }
Refactoring to: 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;
} } }

5. Replace nested conditional with guard clauses
Application scenario: the conditional logic in the function makes it difficult to see the normal execution path. Use guard statements to represent all special situations.
There are two forms of conditional expression: a) all branches belong to normal behavior; b) only one branch of expression is normal behavior, and the others are uncommon. If both branches behave normally, you should use conditional expressions like if… Else… If a condition is extremely rare, you should check the condition separately and return it from the function as soon as the condition is true. Such a separate check is often referred to as a “guard statement.”.
Example:
double getPayAmount() {
  double result; if(mIsDead) {
    result = deadAmount();
  } else {
    if(mIsSeparated) {
      result = separatedAmount();
    } else {
      if(mIsRetired) {
        result = retiredAmount();
      } else {
        result = normalPayAmount();
} } }
return result;
}

Refactoring as follows:
double getPayAmount() {
  if(mIsDead) {
    return deadAmount();
  }
  if(mIsSeparated) {
    return separatedAmount();
  }
  if(mIsRetired) {
    return retiredAmount();
  }
  return normalPayAmount();
}

6. Replace conditional with polymorphism
Application scenario: you have a conditional expression in your hand, which selects different behaviors according to different object types. Put each branch of this conditional expression into an override function within a subclass, and then declare the original function as an abstract function.
Example:
double getSpeed() {
  switch(mType) {
    case EUROPEAN: return getBaseSpeed();
    case AFRICAN: return getBaseSpeed() – getLoadFactor() * mNumberOfCoconuts;
    case NORWEGIAN_BLUE: return mIsNailed ? 0 : getBaseSpeed(mVoltage);
  }
  throw new RuntimeException(“Should be unreachable.”);
}

Refactoring as follows:
abstract class Bird {
  abstract double getSpeed();
}

class European extends Bird {
  double getSpeed() {
  return getBaseSpeed();
} }

class African extends Bird() {
  double getSpeed() {
  return getBaseSpeed() – getLoadFactor() * mNumberOfCoconuts;
} }

class NorwegianBlue extends Bird {
  double getSpeed() {
  return mIsNailed ? 0 : getBaseSpeed(mVoltage);
} }

7. Introduce null object
Application scenario: you need to check whether an object is null again and again, and replace the null value with a null object.
Example:
if (custom == null) {
plan = BillingPlan.basic();
} else {
plan = custom.getPlan();
}
Refactoring as follows:
class Custom {
public Plan getPlan() {
return normalPlan;
}

public boolean isNull() {
return false;
}
}

class NullCustom extends Custom {
public Plan getPlan() {
return BillingPlan.basic();
}

public boolean isNull() {
return true;
}
}

8. Introduce assertion
Application scenario: a piece of code needs to make some assumptions about the state of the program. This assumption is explicitly expressed in assertions.