Заміна методу об'єктом методів
Проблема
У вас є довгий метод, в якому локальні змінні так сильно переплетені, що це робить неможливим застосування відокремлення методу.
Рішення
Перетворіть метод в окремий клас так, щоб локальні змінні стали полями цього класу. Після цього можна без проблем розділити метод на частини.
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.
}
}
Причини рефакторингу
Метод занадто довгий, і ви не можете його розділити через переплетення локальних змінних, які складно ізолювати одну від одної.
Першим кроком до вирішення проблеми буде виділення всього цього методу в окремий клас і перетворення його локальних змінних на поля.
По-перше, це дозволить вам ізолювати проблему в межах цього класу, а по-друге, розчистить дорогу для розділення великого методу на менші за розміром методи, які, до того ж, не підходили б до контексту оригінального класу.
Переваги
- Ізоляція довгого методу у власному класі дозволяє зупинити безконтрольне зростання методу. Крім того, дає можливість ділити його на підметоди в рамках свого класу, не засмічуючи службовими методами оригінальний клас.
Недоліки
- Створюється ще один клас, підвищуючи загальну складність програми.
Порядок рефакторингу
-
Створіть новий клас. Дайте йому назву, ґрунтуючись на призначенні методу, над яким проводиться рефакторинг.
-
У новому класі створіть приватне поле для зберігання посилання на екземпляр класу, в якому раніше знаходився метод. Це посилання згодом можна використати, щоб взяти з оригінального об’єкта якісь допоміжні дані.
-
Створіть окреме приватне поле для кожної локальної змінної методу.
-
Створіть конструктор, який приймає в параметрах значення всіх локальних змінних методу, а також ініціалізує відповідні приватні поля.
-
Оголосіть основний метод і скопіюйте в нього код оригінального методу, замінивши локальні змінні приватними полями.
-
Замініть тіло оригінального методу в початковому класі створенням об’єкта-методу і викликом його основного методу.