Summer SALE

Замена вложенных условных операторов граничным оператором

Также известен как: Replace Nested Conditional with Guard Clauses

Проблема

У вас есть группа вложенных условных операторов, среди которых сложно выделить нормальный ход выполнения кода.

Решение

Выделите все проверки специальных или граничных случаев выполнения в отдельные условия и поместите их перед основными проверками. В идеале, вы должны получить «плоский» список условных операторов, идущих один за другим.

До
public double getPayAmount() { double result; if (isDead){ result = deadAmount(); } else { if (isSeparated){ result = separatedAmount(); } else { if (isRetired){ result = retiredAmount(); } else{ result = normalPayAmount(); } } } return result; }
После
public double getPayAmount() { if (isDead){ return deadAmount(); } if (isSeparated){ return separatedAmount(); } if (isRetired){ return retiredAmount(); } return normalPayAmount(); }
До
public double GetPayAmount() { double result; if (isDead) { result = DeadAmount(); } else { if (isSeparated) { result = SeparatedAmount(); } else { if (isRetired) { result = RetiredAmount(); } else { result = NormalPayAmount(); } } } return result; }
После
public double GetPayAmount() { if (isDead) { return DeadAmount(); } if (isSeparated) { return SeparatedAmount(); } if (isRetired) { return RetiredAmount(); } return NormalPayAmount(); }
До
function getPayAmount() { if ($this->isDead) { $result = $this->deadAmount(); } else { if ($this->isSeparated) { $result = $this->separatedAmount(); } else { if ($this->isRetired) { $result = $this->retiredAmount(); } else { $result = $this->normalPayAmount(); } } } return $result; }
После
function getPayAmount() { if ($this->isDead) { return $this->deadAmount(); } if ($this->isSeparated) { return $this->separatedAmount(); } if ($this->isRetired) { return $this->retiredAmount(); } return $this->normalPayAmount(); }
До
def getPayAmount(self): if self.isDead: result = deadAmount() else: if self.isSeparated: result = separatedAmount() else: if self.isRetired: result = retiredAmount() else: result = normalPayAmount() return result
После
def getPayAmount(self): if self.isDead: return deadAmount() if self.isSeparated: return separatedAmount() if self.isRetired: return retiredAmount() return normalPayAmount()
До
getPayAmount(): number { let result: number; if (isDead){ result = deadAmount(); } else { if (isSeparated){ result = separatedAmount(); } else { if (isRetired){ result = retiredAmount(); } else{ result = normalPayAmount(); } } } return result; }
После
getPayAmount(): number { if (isDead){ return deadAmount(); } if (isSeparated){ return separatedAmount(); } if (isRetired){ return retiredAmount(); } return normalPayAmount(); }

Причины рефакторинга

«Условный оператор из ада» довольно просто отличить. Отступы каждого из уровней вложенности формируют в нем отчётливую стрелку, указывающую вправо:

 if () { if () { do { if () { if () { if () { ... } } ... } ... } while (); ... } else { ... } } 

Разобраться в том, что и как делает такой оператор довольно сложно, так как «нормальный» ход выполнения в нем не очевиден. Такие операторы появляются эволюционным путём, когда каждое из условий добавляется в разные промежутки времени без мыслей об оптимизации остальных условий.

Чтобы упростить такой оператор, нужно выделить все особые случаи в отдельные условные операторы, которые бы при наступлении граничных условий, сразу заканчивали выполнение и возвращали нужное значение. По сути, ваша цель — сделать такой оператор плоским.

Порядок рефакторинга

Постарайтесь избавиться от «побочных эффектов» в условиях операторов. Разделение запроса и модификатора может в этом помочь. Такое решение понадобится для дальнейших перестановок условий.

  1. Выделите граничные условия, которые приводят к вызову исключения или немедленному возвращению значения из метода. Переместите эти условия в начало метода.

  2. После того как с переносами покончено, и все тесты стали проходить, проверьте, можно ли использовать объединение условных операторов для граничных условных операторов, ведущих к одинаковым исключениям или возвращаемым значениям.