Извлечение подкласса
Проблема
Класс имеет фичи, которые используются только в определённых случаях.
Решение
Создайте подкласс и используйте его в этих случаях.
Причины рефакторинга
В основном классе находятся методы и поля для реализации какого-то редкого случая использования класса. Этот случай, хоть и очень редкий, является частью обязанностей класса, поэтому все связанные с ним поля и методы неправильно было бы вынести в абсолютно другой класс. Но, с другой стороны, их можно вынести в подкласс с помощью этого рефакторинга.
Достоинства
-
Создать подкласс довольно легко и быстро.
-
Можно выделить несколько разных подклассов, если основной класс реализует несколько подобных особых случаев.
Недостатки
-
Несмотря на всю очевидную простоту, Наследование может завести вас в тупик, если придётся выделить несколько различных иерархий классов. Например, если у вас был класс
Собаки
, поведение которого отличается в зависимости от размера и длины шерсти собак. Вы выделили из него две иерархии:-
по размеру:
Большие
,Средние
иМаленькие
-
по длине шерсти:
Гладкошёрстные
иДлинношёрстные
-
-
И все бы хорошо, но у вас начнутся проблемы, когда нужно будет создать одновременно
Большую
иГладкошёрстную
собаку, так как объект можно создать лишь из одного класса. С другой стороны, эту проблему можно обойти, используя Композицию вместо Наследования (см. паттерн Стратегия). Другими словами, классСобака
будет иметь два поля-компонента — размер и длина шерсти. В эти поля вы будете подставлять объекты-компоненты необходимых классов. Например, вы сможете создатьСобаку
, имеющуюБольшойРазмер
иДлиннуюШерсть
.
Порядок рефакторинга
-
Создайте новый подкласс из интересующего вас класса.
-
Если для создания объектов из подкласса будут нужны какие-то дополнительные данные, создайте конструктор и дополните его нужными параметрами. Не забудьте вызвать родительскую реализацию конструктора.
-
Найдите все вызовы конструктора родительского класса. В тех случаях, когда требуется функциональность подкласса, замените родительский конструктор конструктором подкласса.
-
Переместите нужные методы и поля из родительского класса в подкласс. Используйте для этого спуск метода и спуск поля. Проще всего начинать перенос с методов. Так поля будут доступны для них все время: из родительского класса до переноса, и из самого подкласса после окончания переноса.
-
После того как подкласс готов, найдите все старые поля, которые управляли тем, какой набор функций должен выполняться. Эти поля можно удалить, заменив полиморфизмом все условные операторы, в которых они использовались. Простой пример — у вас в классе Автомобиль было поле
isElectricCar
, и в зависимости от него, в методеrefuel()
в машину либо заливается бензин, либо заряжается электричество. В результате рефакторинга, полеisElectricCar
будет удалено, а классыАвтомобиль
иЭлектроАвтомобиль
будут иметь свои реализации методаrefuel()
.