В сегодняшнем сценарии подкласс и суперкласс не очень отличаются: эволюция кода привела их к этой ситуации. Поведение было удалено или перенесено в другое место в системе, и это почти одно и то же лицо.
Collapse Иерархия рефакторинга предоставляет ряд шагов для объединения двух таких классов в один, что упрощает конструкцию.
Зачем удалять класс?
Конструкцию, в которой меньше движущихся частей, проще понять и немного сложнее сломать. Дополнительный объект, который не представляет концепцию домена и не обеспечивает ценность путем устранения дублирования, просто добавляет случайную сложность в проект.
Сохранение иерархии на как можно меньшем количестве уровней также полезно, так как это позволяет избежать перехода вверх и вниз по иерархии, чтобы определить, где определяется вызываемый метод. Даже когда он создает длинный класс, он может быть шагом для выхода из локального минимума: сформировать уникальный класс, который можно разбить на маленькие части с композицией.
Предположение этого рефакторинга состоит в том, что в иерархии нет других подклассов; если они есть, теперь они должны расширить объединенный класс, но вам следует искать возможные нарушения принципа подстановки Лискова . Например, в транспортной области некорректно объединять класс Vehicle в его Coach подкласса, в то время как в результате родственный класс Car расширяет Coach.
меры
- Выберите, какой класс удалить : подкласс или суперкласс.
- Использование тянуть вверх или толкать вниз реорганизации , пока один из классов не пусто.
- Все ссылки (в основном, экземпляры, но также подсказки типов и докблоки) теперь должны ссылаться на класс, который вы храните.
- Удалите пустой класс , который на данный момент является только мертвым кодом.
Между шагами 2 и 3 тесты могут не пройти; Выполните их в короткие итерации, чтобы убедиться, что вы всегда в рабочем состоянии. Ни подтягивание, ни подталкивание не являются общим решением для сохранения тестов зеленым: существуют контрпримеры для обоих случаев разрыва ссылок, таких как создание экземпляра суперкласса или подсказка типа для подкласса.
пример
В этом примере мы работаем со знакомым классом NewsFeedItem и его подклассом Link. Требования к ним немногочисленны, и никаких других подклассов не задействовано; на самом деле сам по себе NewsFeedItem не нужен, и он также может быть абстрактным.
<?php
class CollapseHierarchy extends PHPUnit_Framework_TestCase
{
public function testALinkShowsItsAuthor()
{
$link = new Link("/posts/php-refactoring", "giorgiosironi");
$this->assertEquals("<a href=\"/posts/php-refactoring\">/posts/php-refactoring</a> -- @giorgiosironi",
$link->toHtml());
}
}
class NewsFeedItem
{
protected $content;
protected $author;
public function __construct($content, $author)
{
$this->content = $content;
$this->author = '@' . ltrim($author, '@');
}
/**
* @return string an HTML printable version
*/
public function __toString()
{
return "$this->content -- $this->author";
}
}
class Link extends NewsFeedItem
{
public function toHtml()
{
return "<a href=\"$this->content\">$this->content</a> -- $this->author";
}
}
Мы решили удалить суперкласс. Мы нажимаем все вниз, но мы должны искать ссылки, такие как new NewsFeedItem (…), чтобы исправить это, прежде чем выполнить этот шаг.
class NewsFeedItem
{
}
class Link extends NewsFeedItem
{
protected $content;
protected $author;
public function __construct($content, $author)
{
$this->content = $content;
$this->author = '@' . ltrim($author, '@');
}
/**
* @return string an HTML printable version
*/
public function __toString()
{
return "$this->content -- $this->author";
}
public function toHtml()
{
return "<a href=\"$this->content\">$this->content</a> -- $this->author";
}
}
Теперь мы можем удалить суперкласс вместе с ключевым словом extends в Link.
class Link
{
protected $content;
protected $author;
public function __construct($content, $author)
{
$this->content = $content;
$this->author = '@' . ltrim($author, '@');
}
/**
* @return string an HTML printable version
*/
public function __toString()
{
return "$this->content -- $this->author";
}
public function toHtml()
{
return "<a href=\"$this->content\">$this->content</a> -- $this->author";
}
}