Base/Knowledges/IT/Рефакторинг/Методы рефакторинга/Замена условной инструкции полиморфизмом (Replace Conditional with Polymorphism).md
2026-02-23 19:52:05 +03:00

6.2 KiB
Raw Blame History

#рефакторинг #методы_рефакторинга

!Pasted image 20250207085637.png

Проблема

У вас есть условный оператор, который, в зависимости от типа или свойств объекта, выполняет различные действия.

class Bird {
  // ...
  getSpeed(): number {
    switch (type) {
      case EUROPEAN:
        return getBaseSpeed();
      case AFRICAN:
        return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
      case NORWEGIAN_BLUE:
        return (isNailed) ? 0 : getBaseSpeed(voltage);
    }
    throw new Error("Should be unreachable");
  }
}

Решение

Создайте подклассы, которым соответствуют ветки условного оператора. В них создайте общий метод и переместите в него код из соответствующей ветки условного оператора. Впоследствии замените условный оператор на вызов этого метода. Таким образом, нужная реализация будет выбираться через полиморфизм в зависимости от класса объекта.

abstract class Bird {
  // ...
  abstract getSpeed(): number;
}

class European extends Bird {
  getSpeed(): number {
    return getBaseSpeed();
  }
}
class African extends Bird {
  getSpeed(): number {
    return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
  }
}
class NorwegianBlue extends Bird {
  getSpeed(): number {
    return (isNailed) ? 0 : getBaseSpeed(voltage);
  }
}

// Somewhere in client code
let speed = bird.getSpeed();

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

В коде есть условные операторы, которые выполняют различную работу, в зависимости от:

  • класса объекта или интерфейса, который он реализует
  • значения какого-то из полей объекта
  • результата вызова одного из методов объекта При этом если у вас появится новый тип или свойство объекта, нужно будет искать и добавлять код во все схожие условные операторы. Таким образом, польза от данного рефакторинга увеличивается, если условных операторов больше одного, и они разбросаны по всем методам объекта.

Достоинства

  • вместо того, чтобы спрашивать объект о его состоянии, а потом выполнять на основании этого какие-то действия, гораздо проще просто сказать ему, что нужно делать, а как это делать он решит сам.
  • Убивает дублирование кода. Вы избавляетесь от множества почти одинаковых условных операторов
  • Если вам потребуется добавить новый вариант выполнения, все, что придётся сделать, это добавить новый подкласс, не трогая существующий код (Принцип открытости-закрытости).

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

Подготовка к рефакторингу Чтобы выполнить этот рефакторинг, вам следует иметь готовую иерархию классов, в которых будут содержаться альтернативные поведения. Если такой иерархии ещё нет, нужно создать её. В этом могут помочь другие рефакторинги:

  • Замена кода типа подклассами (Replace Type Code with Subclasses). При этом для всех значений какого-то свойства объекта будут созданы свои подклассы. Это хоть и простой, но менее гибкий способ, так как нельзя будет создать подклассы для других свойств объекта.

  • Замена кодирования типа состоянием/стратегией. При этом для определенного свойства объекта будет выделен свой класс и из него созданы подклассы для каждого значения этого свойства. Текущий класс будет содержать ссылки на объекты такого типа и делегировать им выполнение.

Последующие шаги этого рефакторинга подразумевают, что вы уже создали иерархию.

  1. Если условный оператор находится в методе, который выполняет ещё какие-то действия, Извлечение функции (Extract Function).
  2. Для каждого подкласса иерархии, переопределите метод, содержащий условный оператор, и скопируйте туда код соответствующей ветки оператора.
  3. Удалите эту ветку из условного оператора
  4. Повторяйте замену, пока условный оператор не опустеет. Затем удалите условный оператор и объявите метод абстрактным.

Борется с запахом

Ссылки