Статьи

Практический рефакторинг PHP: метод извлечения

Я начинаю новую серию: Практический рефакторинг PHP. Каждая статья будет посвящена одному из рефакторингов, определенных Фаулером в его классической книге, применительно к PHP-коду.

Извлечь метод означает создание нового метода, который будет содержать часть существующего кода: это один из самых основных способов рефакторинга, который вы должны выполнять, точно так же, как каждый повар может нарезать овощи или включать газ. Это строительный блок для более сложных рефакторингов.

Многие, многие проблемы возникают из-за того, что методы (или стали) слишком длинными, или которые путают разные концепции в одном и том же блоке кода.

Почему я должен извлечь метод?

Метод извлечения — один из самых простых инструментов для инкапсуляции. Это упрощает область видимости, поскольку все переменные, определенные внутри метода, не смогут загрязнить вызывающий код. Рефакторинг называется «Метод извлечения», поскольку он касается объектно-ориентированного программирования, но функция извлечения будет иметь то же значение.

Извлечение метода заставляет вас определять контракт с помощью фрагмента кода, понимая входные данные (параметры метода) и выходные данные (возвращаемое значение). Это не настоящий дизайн по контракту, но он подходит для 80% случаев .

Наконец, извлеченный метод может быть повторно использован независимо от вызывающего кода. Устранение дублирования является одной из движущих сил, которая делает большинство методов рефакторинга интересными.

меры

Я следую рецептам Фаулера, но я настрою его под свой стиль разработки (поскольку я пишу примеры кода) и особенности PHP.

1. Создайте метод и выберите значимое имя. Тридцать секунд, проведенные здесь, могут избежать переименования метода doSomething () в тысячах различных вызовов в будущем.

Хорошим вариантом может быть попытка вызвать его с того места, откуда вы хотите извлечь его, чтобы установить подпись, удобную для клиента (например, для TDD). Но, с другой стороны, прокомментируйте вызов еще раз, так как эти шаги не должны ничего сломать. Мы должны перейти из зеленого состояния в другое зеленое состояние.

2. Скопируйте извлеченный код в новый метод. Отсканируйте его и исправьте ссылки на переменные:

  • переменные, существующие до вызова, становятся параметрами метода .
  • Переменные, которые создаются в блоке кода и используются впоследствии, являются частью возвращаемого значения . Обычно это только одна переменная, но если их несколько, вы можете обернуть их в объект или (временно) в ассоциативный массив.
  • Локальные переменные теперь могут быть скрыты внутри метода : на них нет ссылок вне кода, и PHP будет собирать их по окончании метода (если, конечно, на них больше нет ссылок).

3. Замените оригинальный код вызовом метода.

4. Выполните переименование или рефакторинг внутри извлеченного метода.

пример

В примере кода показаны различные шаги, выполняемые для запуска кода PHP. Я также предоставляю тест, так как я уже использую его, чтобы проверить, что рефакторинг прошел хорошо.

Мы начнем с метода, который смешивает регулярные выражения с форматированием даты:

<?php
class ExtractMethodTest extends PHPUnit_Framework_TestCase
{
    public function testMethodExtractionShouldNotMakeThisTestFail()
    {
        $logParser = new LogParser();
        $logLine = '127.0.0.1 - - [04/30/2011:17:07:31 +0200] "GET /favicon.ico HTTP/1.1" 404 450 "-" "Mozilla"';
        $day = $logParser->getDayOfTheWeek($logLine);
        $this->assertEquals('On Saturday we got a visit', $day);
    }
}

class LogParser
{
    public function getDayOfTheWeek($logLine)
    {
        preg_match('([0-9]{2}/[0-9]{2}/[0-9]{4})', $logLine, $matches);
        $extractedDate = $matches[0];
        $date = new DateTime($extractedDate);
        return 'On ' . $date->format('l') . ' we got a visit';
    }
}

Мы извлекаем метод, и в стиле TDD мы делаем столько же, сколько требуется, чтобы сохранить зеленый тест. Мы немедленно вызываем этот метод из старого кода: это больший шаг.

<?php
class ExtractMethodTest extends PHPUnit_Framework_TestCase
{
    public function testMethodExtractionShouldNotMakeThisTestFail()
    {
        $logParser = new LogParser();
        $logLine = '127.0.0.1 - - [04/30/2011:17:07:31 +0200] "GET /favicon.ico HTTP/1.1" 404 450 "-" "Mozilla"';
        $day = $logParser->getDayOfTheWeek($logLine);
        $this->assertEquals('On Saturday we got a visit', $day);
    }
}

class LogParser
{
    public function getDayOfTheWeek($logLine)
    {
        $date = $this->getDate($logLine);
        return 'On ' . $date->format('l') . ' we got a visit';
    }

    function getDate($logLine)
    {
        preg_match('([0-9]{2}/[0-9]{2}/[0-9]{4})', $logLine, $matches);
        $extractedDate = $matches[0];
        $date = new DateTime($extractedDate);
        return $date;
    }
}

Наконец, мы уточняем извлеченный метод, определяя его область применения, добавляя докблок и удаляя временные, объясняющие переменные, которые теперь стали бесполезными из-за простоты этого метода.

<?php
class ExtractMethodTest extends PHPUnit_Framework_TestCase
{
    public function testMethodExtractionShouldNotMakeThisTestFail()
    {
        $logParser = new LogParser();
        $logLine = '127.0.0.1 - - [04/30/2011:17:07:31 +0200] "GET /favicon.ico HTTP/1.1" 404 450 "-" "Mozilla"';
        $day = $logParser->getDayOfTheWeek($logLine);
        $this->assertEquals('On Saturday we got a visit', $day);
    }
}

class LogParser
{
    public function getDayOfTheWeek($logLine)
    {
        $date = $this->getDate($logLine);
        return 'On ' . $date->format('l') . ' we got a visit';
    }

    /**
     * @return DateTime
     */
    private function getDate($logLine)
    {
        preg_match('([0-9]{2}/[0-9]{2}/[0-9]{4})', $logLine, $matches);
        return new DateTime($matches[0]);
    }
}