Замена метода объектом методов
Проблема
У вас есть длинный метод, в котором локальные переменные так сильно переплетены, что это делает невозможным применение «извлечения метода».
Решение
Преобразуйте метод в отдельный класс так, чтобы локальные переменные стали полями этого класса. После этого можно без труда разделить метод на части.
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.
}
}
Причины рефакторинга
Метод слишком длинный, и вы не можете его разделить из-за хитросплетения локальных переменных, которые сложно изолировать друг от друга.
Первым шагом к решению проблемы будет выделение всего этого метода в отдельный класс и превращение его локальных переменных в поля.
Во-первых, это позволит вам изолировать проблему в пределах этого класса, а во-вторых, расчистит дорогу для разделения большого метода на методы поменьше, которые, к тому же, не подходили бы к смыслу оригинального класса.
Достоинства
- Изоляция длинного метода в собственном классе позволяет остановить бесконтрольный рост метода. Кроме того, даёт возможность разделить его на подметоды в рамках своего класса, не засоряя служебными методами оригинальный класс.
Недостатки
- Создаётся ещё один класс, повышая общую сложность программы.
Порядок рефакторинга
-
Создайте новый класс. Дайте ему название, основываясь на предназначении метода, который рефакторите.
-
В новом классе создайте приватное поле для хранения ссылки на экземпляр класса, в котором раньше находился метод. Эту ссылку потом можно будет использовать, чтобы брать из оригинального объекта нужные данные, если потребуется.
-
Создайте отдельное приватное поле для каждой локальной переменной метода.
-
Создайте конструктор, который принимает в параметрах значения всех локальных переменных метода, а также инициализирует соответствующие приватные поля.
-
Объявите основной метод и скопируйте в него код оригинального метода, заменив локальные переменные приватным полями.
-
Замените тело оригинального метода в исходном классе созданием объекта-метода и вызовом его основного метода.