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

85 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#рефакторинг #методы_рефакторинга
![[Pasted image 20250207085637.png]]
### **Проблема**
У вас есть условный оператор, который, в зависимости от типа или свойств объекта, выполняет различные действия.
```typescript
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");
}
}
```
### **Решение**
Создайте подклассы, которым соответствуют ветки условного оператора. В них создайте общий метод и переместите в него код из соответствующей ветки условного оператора. Впоследствии замените условный оператор на вызов этого метода. Таким образом, нужная реализация будет выбираться через полиморфизм в зависимости от класса объекта.
```typescript
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. Повторяйте замену, пока условный оператор не опустеет. Затем удалите условный оператор и объявите метод абстрактным.
### **Борется с запахом**
- [[Повторяющиеся switch]]
### **Ссылки**
- https://refactoring.guru/ru/replace-conditional-with-polymorphism
- [[Мартин Фаулер - Рефакторинг кода на JavaScript]]