Також відомий як Replace Method with Method Object

Рефакторинг Заміна методу об'єктом методів

Проблема

У вас є довгий метод, в якому локальні змінні так сильно переплетені, що це робить неможливим застосування «відокремлення методу» (Extract method).

Рішення

Перетворіть метод в окремий клас так, щоб локальні змінні стали полями цього класу. Після цього можна без проблем розділити метод на частини.
До
class Order {
  //...
  public double price() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;
    // 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 order object.
    //...
  }
  
  public double compute() {
    // long computation.
    //...
  }
}
До
public class Order 
{
  //...
  public double Price() 
  {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;
    // 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 order object.
    //...
  }
  
  public double Compute() 
  {
    // long computation.
    //...
  }
}
До
class Order {
  ...
  public function price() {
    $primaryBasePrice = 10;
    $secondaryBasePrice = 20;
    $tertiaryBasePrice = 30;
    // long computation.
    ...
  }
}
Після
class Order {
  ...
  public function price() {
    return new PriceCalculator($this)->compute();
  }
}

class PriceCalculator {
  private $primaryBasePrice;
  private $secondaryBasePrice;
  private $tertiaryBasePrice;
  
  public __construct(Order $order) {
    // copy relevant information from order object.
    ...
  }
  
  public function compute() {
    // long computation.
    ...
  }
}
До
class Order:
    #...
    def price(self):
        primaryBasePrice = 0
        secondaryBasePrice = 0
        tertiaryBasePrice = 0
        # 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 order object.
        #...

    def compute(self):
        # long computation.
        #...

Причини рефакторингу

Метод занадто довгий, і ви не можете його розділити через переплетення локальних змінних, які складно ізолювати одну від одної.

Першим кроком до вирішення проблеми буде виділення всього цього методу в окремий клас і перетворення його локальних змінних на поля.

По-перше, це дозволить вам ізолювати проблему в межах цього класу, а по-друге, розчистить дорогу для розділення великого методу на менші за розміром методи, які, до того ж, не підходили б до контексту оригінального класу.

Переваги

  • Ізоляція довгого методу у власному класі дозволяє зупинити безконтрольне зростання методу. Крім того, дає можливість ділити його на підметоди в рамках свого класу, не засмічуючи службовими методами оригінальний клас.

Недоліки

  • Створюється ще один клас, підвищуючи загальну складність програми.

Порядок рефакторингу

  1. Створіть новий клас. Дайте йому назву, ґрунтуючись на призначенні методу, над яким проводиться рефакторинг.

  2. У новому класі створіть приватне поле для зберігання посилання на екземпляр класу, в якому раніше знаходився метод. Цю ссилку згодом можна використати, щоб взяти з оригінального об'єкту якісь допоміжні дані.

  3. Створіть окреме приватне поле для кожної локальної змінної методу.

  4. Створіть конструктор, який приймає в параметрах значення всіх локальних змінних методу, а також ініціалізує відповідні приватні поля.

  5. Оголосіть основний метод і скопіюйте в нього код оригінального методу, замінивши локальні змінні приватними полями.

  6. Замініть тіло оригінального методу в початковому класі створенням об'єкту-методу і викликом його основного методу.

Замучились читати?

Збігайте за подушкою, в нас тут контенту приблизно на 7 годин читання.

Або спробуйте наш новий інтерактивний курс з рефакторингу. Він більш інформативний та набагато цікавіший за банальний тест.

Дізнатися більше...

Живий приклад

Перший раз у нас? Нічого страшного!

У нас тут все просто – цей інтерактивний приклад дуже схожий на відео (хоча й виглядає набагато крутіше).

  1. Натискаєте велику кнопку "Почати", а далі слідуєте усім підказкам.
  2. Ви можете перемотувати програвання, використовуючи стрілки зліва.
  3. Окрім того, ви можете подивитися різницю між стартовим та отриманим кодом, натиснувши кнопку ока ().
  4. Кнопка компіляції та тестування () дає змогу перевірити код на наявність помилок.