Замена метода объектом методов
Проблема
У вас есть длинный метод, в котором локальные переменные так сильно переплетены, что это делает невозможным применение «извлечения метода».
Решение
Преобразуйте метод в отдельный класс так, чтобы локальные переменные стали полями этого класса. После этого можно без труда разделить метод на части.
class Order { // ... public double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // Perform long computation. } } class Order { // ... public double price() { return new PriceCalculator(this).compute(); } } class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice; public PriceCalculator(Order order) { // Copy relevant information from the // order object. } public double compute() { // Perform long computation. } } public class Order { // ... public double Price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // Perform long computation. } } public class Order { // ... public double Price() { return new PriceCalculator(this).Compute(); } } public class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice; public PriceCalculator(Order order) { // Copy relevant information from the // order object. } public double Compute() { // Perform long computation. } } class Order { // ... public function price() { $primaryBasePrice = 10; $secondaryBasePrice = 20; $tertiaryBasePrice = 30; // Perform long computation. } } class Order { // ... public function price() { return (new PriceCalculator($this))->compute(); } } class PriceCalculator { private $primaryBasePrice; private $secondaryBasePrice; private $tertiaryBasePrice; public function __construct(Order $order) { // Copy relevant information from the // order object. } public function compute() { // Perform long computation. } } class Order: # ... def price(self): primaryBasePrice = 0 secondaryBasePrice = 0 tertiaryBasePrice = 0 # Perform long computation. class Order: # ... def price(self): return PriceCalculator(self).compute() class PriceCalculator: def __init__(self, order): self._primaryBasePrice = 0 self._secondaryBasePrice = 0 self._tertiaryBasePrice = 0 # Copy relevant information from the # order object. def compute(self): # Perform long computation. class Order { // ... price(): number { let primaryBasePrice; let secondaryBasePrice; let tertiaryBasePrice; // Perform long computation. } } class Order { // ... price(): number { return new PriceCalculator(this).compute(); } } class PriceCalculator { private _primaryBasePrice: number; private _secondaryBasePrice: number; private _tertiaryBasePrice: number; constructor(order: Order) { // Copy relevant information from the // order object. } compute(): number { // Perform long computation. } } Причины рефакторинга
Метод слишком длинный, и вы не можете его разделить из-за хитросплетения локальных переменных, которые сложно изолировать друг от друга.
Первым шагом к решению проблемы будет выделение всего этого метода в отдельный класс и превращение его локальных переменных в поля.
Во-первых, это позволит вам изолировать проблему в пределах этого класса, а во-вторых, расчистит дорогу для разделения большого метода на методы поменьше, которые, к тому же, не подходили бы к смыслу оригинального класса.
Достоинства
- Изоляция длинного метода в собственном классе позволяет остановить бесконтрольный рост метода. Кроме того, даёт возможность разделить его на подметоды в рамках своего класса, не засоряя служебными методами оригинальный класс.
Недостатки
- Создаётся ещё один класс, повышая общую сложность программы.
Порядок рефакторинга
-
Создайте новый класс. Дайте ему название, основываясь на предназначении метода, который рефакторите.
-
В новом классе создайте приватное поле для хранения ссылки на экземпляр класса, в котором раньше находился метод. Эту ссылку потом можно будет использовать, чтобы брать из оригинального объекта нужные данные, если потребуется.
-
Создайте отдельное приватное поле для каждой локальной переменной метода.
-
Создайте конструктор, который принимает в параметрах значения всех локальных переменных метода, а также инициализирует соответствующие приватные поля.
-
Объявите основной метод и скопируйте в него код оригинального метода, заменив локальные переменные приватным полями.
-
Замените тело оригинального метода в исходном классе созданием объекта-метода и вызовом его основного метода.