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