🎉 Ура! Після трьох років роботи, я нарешті випустив англійську версію книжки про патерни! Ось вона »
Також відомий як: Replace Method with Method Object

Заміна методу об’єктом методів

Проблема

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

Рішення

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

До
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.
  }
}

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

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

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

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

Переваги

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

Недоліки

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

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

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

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

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

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

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

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