Autumn SALE

Введення зовнішнього методу

Також відомий як: Introduce Foreign Method

Проблема

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

Рішення

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

До
class Report {
  // ...
  void sendReport() {
    Date nextDay = new Date(previousEnd.getYear(),
      previousEnd.getMonth(), previousEnd.getDate() + 1);
    // ...
  }
}
Після
class Report {
  // ...
  void sendReport() {
    Date newStart = nextDay(previousEnd);
    // ...
  }
  private static Date nextDay(Date arg) {
    return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
  }
}
До
class Report 
{
  // ...
  void SendReport() 
  {
    DateTime nextDay = previousEnd.AddDays(1);
    // ...
  }
}
Після
class Report 
{
  // ...
  void SendReport() 
  {
    DateTime nextDay = NextDay(previousEnd);
    // ...
  }
  private static DateTime NextDay(DateTime date) 
  {
    return date.AddDays(1);
  }
}
До
class Report {
  // ...
  public function sendReport() {
    $previousDate = clone $this->previousDate;
    $paymentDate = $previousDate->modify("+7 days");
    // ...
  }
}
Після
class Report {
  // ...
  public function sendReport() {
    $paymentDate = self::nextWeek($this->previousDate);
    // ...
  }
  /**
   * Foreign method. Should be in Date.
   */
  private static function nextWeek(DateTime $arg) {
    $previousDate = clone $arg;
    return $previousDate->modify("+7 days");
  }
}
До
class Report:
    # ...
    def sendReport(self):
        nextDay = Date(self.previousEnd.getYear(),
            self.previousEnd.getMonth(), self.previousEnd.getDate() + 1)
        # ...
Після
class Report:
    # ...
    def sendReport(self):
        newStart = self._nextDay(self.previousEnd)
        # ...
        
    def _nextDay(self, arg):
        return Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1)
До
class Report {
  // ...
  sendReport(): void {
    let nextDay: Date = new Date(previousEnd.getYear(),
      previousEnd.getMonth(), previousEnd.getDate() + 1);
    // ...
  }
}
Після
class Report {
  // ...
  sendReport() {
    let newStart: Date = nextDay(previousEnd);
    // ...
  }
  private static nextDay(arg: Date): Date {
    return new Date(arg.getFullYear(), arg.getMonth(), arg.getDate() + 1);
  }
}

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

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

Цей рефакторинг найвигідніше використовувати у випадках, коли ділянка коду, який ви хочете перенести в метод, повторюється кілька разів в різних місцях програми.

Оскільки ви передаєте об’єкт службового класу в параметри нового методу, у вас є доступ до всіх його полів. Ви можете робити всередині цього методу практично все, що вам може бути потрібно, начебто метод є частиною службового класу.

Переваги

  • Прибирає дублювання коду. Якщо ваша ділянка коду повторюється в декількох місцях, ви можете замінити її викликом методу. Це зручніше за дублювання навіть з урахуванням того, що зовнішній метод знаходиться не там, де б хотілось.

Недоліки

  • Причини того, чому метод службового класу знаходиться в клієнтському класі не завжди очевидні для того фахівця, який підтримуватиме код після вас. Якщо цей метод може бути використаний і в інших класах, має сенс створити обгортку над службовим класом, і помістити метод туди. Те ж саме має сенс зробити, якщо таких службових методів декілька. У цьому допоможе рефакторинг введення локального розширення.

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

  1. Створіть новий метод в клієнтському класі.

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

  3. Витягніть ділянки коду, які вам потрібні, в цей метод і замініть їх викликами методу.

  4. Обов’язково залиште в коментарі до цього методу мітку Foreight method і заклик перенести цей метод в службовий клас, якщо така можливість з’явиться надалі. Це полегшить розуміння того, чому метод знаходиться в цьому класі для тих, хто підтримуватиме програмний продукт в майбутньому.