Також відомий як 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();
}
До
public 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;
}
Після
public 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()

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

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

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

Розібратися в тому, що і як робить такий оператор досить складно, оскільки «нормальний» хід виконання в ньому не очевидний. Такі оператори з'являються еволюційним шляхом, коли кожна з умов додається в різні часи без думки про необхідності оптимизаціі інших умов.

Щоб спростити такий оператор, треба виділити усі особливі випадки в окремі умовні оператори, які у випадках настання граничних умов будуть відразу закінчувати виконання і повертатимуть потрібне значення. По суті, ваша мета - зробити такий оператор пласким.

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

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

  1. Виділіть граничні умови, які призводять до виклику виключення або негайного повернення значення з методу. Перемістіть ці умови в початок методу.

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

Замучились читати?

Збігайте за подушкою, в нас тут контенту приблизно на 7 годин читання.

Або спробуйте наш новий інтерактивний курс з рефакторингу. Він більш інформативний та набагато цікавіший за банальний тест.

Дізнатися більше...

Живий приклад

Перший раз у нас? Нічого страшного!

У нас тут все просто – цей інтерактивний приклад дуже схожий на відео (хоча й виглядає набагато крутіше).

  1. Натискаєте велику кнопку "Почати", а далі слідуєте усім підказкам.
  2. Ви можете перемотувати програвання, використовуючи стрілки зліва.
  3. Окрім того, ви можете подивитися різницю між стартовим та отриманим кодом, натиснувши кнопку ока ().
  4. Кнопка компіляції та тестування () дає змогу перевірити код на наявність помилок.