В сегодняшнем сценарии подкласс и суперкласс не очень отличаются: эволюция кода привела их к этой ситуации. Поведение было удалено или перенесено в другое место в системе, и это почти одно и то же лицо.
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"; } }