85 lines
6.2 KiB
Markdown
85 lines
6.2 KiB
Markdown
#рефакторинг #методы_рефакторинга
|
||
|
||
![[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]] |