Весняний РОЗПРОДАЖ

Заміна вкладених умовних операторів граничним оператором

Також відомий як: 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. Після того, як з перенесеннями покінчено, і усі тести стали проходити, перевірте, чи можна використати ооб’єднання умовних операторів для граничних умовних операторів, що ведуть до однакових виключень або значень, які повертаються.