Делегирование является более гибким решением в отношении наследования, поскольку оно позволяет изменять коллабораторов путем введения новых классов. Тем не менее, он скрывает публичный протокол соавтора в частном поле.
Если вы обнаружите, что пишете много методов делегирования, вы можете изменить рефакторинг для упрощения кода. Подкласс унаследует все автоматически, что делает наследование мощным оружием.
Зачем двигаться в сторону наследования?
Фаулер называет рост кода делегирования силой, требующей такого рефакторинга. Не все проблемы можно точно смоделировать с помощью делегирования, хотя это эквивалентно наследованию. В зависимости от домена и сущности, вы можете предпочесть отношения наследования Vehicle ^ -Coach между классами (где Vehicle содержит поле типа « водитель» ) или Coach — *> «Состав водителя».
Есть и другие причины для выбора одного над другим, например изменяемое состояние в соавторе . В случае наследования он не может быть разделен между объектами, в то время как в делегировании он может.
Первым следствием является то , что если вы хотите , чтобы избежать наложения и один объект изменения состояния другого, наследство будет остановить это происходит точно.
Второе следствие в том , что если вы хотите на сегодняшний день доли между объектами таким образом , вместо этого, вы не можете использовать наследование.
Предупреждение: все открытые методы будут унаследованы. Если вы не используете их все, вы нарушаете LSP путем рефакторинга наследования, злоупотребляя этой конструкцией. Более того, наследование — это однократная стратегия: вы можете использовать его только один раз для класса, а затем другие обязанности должны учитывать только композицию.
меры
Шаги выполняют обратную процедуру Заменить Наследование с Делегированием .
- Заставьте объект клиента расширять класс делегата .
- Делегат должен стать $ это путем инициализации в конструкторе клиента.
- Удалить делегирование : методы теперь должны быть унаследованы; меняйте один метод за раз, если вы не уверены в эффектах.
- Удалите поле делегата и удалите ненужные косвенные указания.
пример
Мы начинаем с конца предыдущего примера, но он привел нас в нужное русло. Большая часть состояния была перенесена в формат из-за желания устранить дублирование полей в подклассы (текст / контент, автор). Кстати, это уже не формат, но начинает появляться имя, подобное FeedItem …
__toString () — это пример метода, непосредственно делегированного объекту формата, но в реальном коде их может быть (и будет) гораздо больше ,
<?php class ReplaceDelegationWithInheritance extends PHPUnit_Framework_TestCase { public function testAPostShowsItsAuthor() { $post = new Post("Hello, world!", "giorgiosironi"); $this->assertEquals("Hello, world! -- giorgiosironi", $post->__toString()); } public function testALinkShowsItsAuthor() { $link = new Link("http://en.wikipedia.com", "giorgiosironi"); $this->assertEquals("<a href=\"http://en.wikipedia.com\">http://en.wikipedia.com</a> -- giorgiosironi", $link->__toString()); } } class TextSignedByAuthorFormat { private $text; private $author; public function __construct($text, $author) { $this->text = $text; $this->author = $author; } public function __toString() { return "$this->text -- $this->author"; } } class Post { private $format; public function __construct($text, $author) { $this->format = new TextSignedByAuthorFormat($text, $author); } public function __toString() { return $this->format->__toString(); } } class Link { private $format; public function __construct($url, $author) { $this->format = new TextSignedByAuthorFormat($this->displayedText($url), $author); } protected function displayedText($url) { return "<a href=\"$url\">$url</a>"; } public function __toString() { return $this->format->__toString(); } }
Расширяя класс делегата, мы доказываем, что он совместим с будущим подклассом:
class TextSignedByAuthorFormat { private $text; private $author; public function __construct($text, $author) { $this->text = $text; $this->author = $author; } public function __toString() { return "$this->text -- $this->author"; } } class Post extends TextSignedByAuthorFormat { private $format; public function __construct($text, $author) { $this->format = new TextSignedByAuthorFormat($text, $author); } public function __toString() { return $this->format->__toString(); } } class Link extends TextSignedByAuthorFormat { private $format; public function __construct($url, $author) { $this->format = new TextSignedByAuthorFormat($this->displayedText($url), $author); } protected function displayedText($url) { return "<a href=\"$url\">$url</a>"; } public function __toString() { return $this->format->__toString(); } }
Мы модифицируем __construct () и удаляем делегирование в методах. Если мы этого не сделаем, вызов будет повторяться бесконечно, так как $ this-> method () вызовет $ this-> format-> method (), который на самом деле является $ this-> method (), и снова начнет цикл. Xdebug обнаружит эти проблемы и заблокирует скрипт на максимальном уровне вложенности.
class Post extends TextSignedByAuthorFormat { private $format; public function __construct($text, $author) { parent::__construct($text, $author); } } class Link extends TextSignedByAuthorFormat { private $format; public function __construct($url, $author) { parent::__construct($this->displayedText($url), $author); } protected function displayedText($url) { return "<a href=\"$url\">$url</a>"; } }
Теперь мы можем упростить, удалив поле делегата, переименовав суперкласс и удалив конструкторы, которые просто делегируют. Опять же, делегирующие методы должны были быть удалены на предыдущем шаге, если только они не заменили $ this-> format-> на parent :: , которые делегирующие методы никогда не используют вместо этого.
class FeedItem { private $text; private $author; public function __construct($text, $author) { $this->text = $text; $this->author = $author; } public function __toString() { return "$this->text -- $this->author"; } } class Post extends FeedItem {} class Link extends FeedItem { public function __construct($url, $author) { parent::__construct($this->displayedText($url), $author); } protected function displayedText($url) { return "<a href=\"$url\">$url</a>"; } }