Статьи

Практический рефакторинг PHP: встроенный класс

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

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

Если извлечение удаляет дублирование, не приводит ли inlinine к дублированию?

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

В правилах простой конструкции говорят нам , когда встраивание становится важным, тоже. Согласно правилам, наша работа заканчивается, когда проектирование:

  1. Запускает все тесты.
  2. Выражает каждую идею, которую нам нужно выразить.
  3. Говорит все один раз и только один раз ..
  4. Не имеет лишних частей.

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

меры

  1. Дублируйте все открытые сигнатуры методов класса, чтобы удалить их во поглощающую (номенклатуру Фаулера). Эти методы должны просто делегировать исходный класс на данный момент. Методы могут быть объявлены как закрытые, если это единственный класс, использующий исходный.
  2. Измените ссылки с $ this- >laborator-> method () на $ this-> method ().
  3. Примените Move Method и Move Field, чтобы поместить мясо в эти методы.
  4. Удалите исходный класс: в это время должна быть пустая оболочка.

пример

Мы начинаем с ситуации, аналогичной окончанию примера Extract Class. На этот раз требование сложного форматирования было удалено, и MoneyAmount не несет большой ответственности. Встроенный класс занимает много строк кода, чтобы выполнить только конкатенацию.

<?php
class ExtractClassTest extends PHPUnit_Framework_TestCase
{
    public function testDisplaysMoneyInAHumanFormat()
    {
        // using strings for representation to avoid loss of precision
        $moneyAmount = new MoneySpan(new MoneyAmount('100'));
        $this->assertEquals('<span class="money">100.00</span>', $moneyAmount->toHtml());
    }
}

class MoneySpan
{
    /**
     * @param int $amount
     */
    public function __construct(MoneyAmount $amount)
    {
        $this->amount = $amount;
    }

    public function toHtml()
    {
        $html = '<span class="money">' . $this->amount->format() . '</span>';
        return $html;
    }
}

class MoneyAmount
{
    private $amount;

    public function __construct($amount)
    {
        $this->amount = $amount;
    }

    public function format()
    {
        return $this->amount . '.00';
    }
}

После рефакторинга у нас меньше классов (1 вместо 2) и, следовательно, меньше методов для обслуживания. Тест изменился на этапе настройки.

<?php
class ExtractClassTest extends PHPUnit_Framework_TestCase
{
    public function testDisplaysMoneyInAHumanFormat()
    {
        // using strings for representation to avoid loss of precision
        $moneyAmount = new MoneySpan('100');
        $this->assertEquals('<span class="money">100.00</span>', $moneyAmount->toHtml());
    }
}

class MoneySpan
{
    /**
     * @param int|string $amount
     * The constructor now takes the arguments of the inline class, too.
     */
    public function __construct($amount)
    {
        $this->amount = $amount;
    }

    /**
     * This method must not refer to the collaborator anymore,
     * but to the imported methods.
     */
    public function toHtml()
    {
        $html = '<span class="money">' . $this->format() . '</span>';
        return $html;
    }

    /**
     * The public format() method of the inline class has been included as
     * a private method. We could go on and inline this method in toHtml()
     * as well.
     */
    private function format()
    {
        return $this->amount . '.00';
    }
}