Наследование
Наследование — один из четырёх важнейших механизмов объектно-ориентированного программирования, позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом.
При наследовании используется ключевое слово extents:
Пример родительского класса:
class Foo { public function printItem($string) { echo 'Foo: ' . $string . PHP_EOL; } public function printPHP() { echo 'PHP is great.' . PHP_EOL; } }
Пример дочернего класса:
class Bar extends Foo { public function printItem($string) { echo 'Bar: ' . $string . PHP_EOL; } }
Создание объектов:
$foo = new Foo(); $bar = new Bar(); $foo->printItem('baz'); // Выведет: 'Foo: baz' $foo->printPHP(); // Выведет: 'PHP is great' $bar->printItem('baz'); // Выведет: 'Bar: baz' $bar->printPHP(); // Выведет: 'PHP is great'
Композиция
«Композиция лучше наследования», сказал великий Конфуций.
Давайте разберемся, почему так...
Пусть нам нужно вывести в браузер части страницы:head, footer и body, реализация которых осуществляется в отедльных классах, а сам вывод делаем с помощью еще одного класса.
class PrintHead { public function print() { echo 'HEAD'; } } class PrintFooter { public function print() { echo 'FOOTER'; } } class PrintBody { public function print() { echo 'BODY'; } } class Print { public function go() { $head = new PrintHead(); $body = new PrintBody(); $footer = new PrintFooter(); $head->print(); $body->print(); $footer->print(); } } $print = new Print(); $print->go(); // HEAD BODY FOOTER
Такой подход и называется композицией (или «агрегирование по значению»). Здесь тонкость в том, что класс Print сам инстанцирует все нужные классы. После того как Print будет уничтожен, будут уничтожены и все созданные им объекты.
Очевидно данный подход более гибкий, и обратите внимание, что здесь нет наследования. Конечно, в более сложных задачах, мы можем использовать наследование, где лучшим вариантом будет задействовать интерфейсы или абстрактные классы, которые позволят «унифицировать» методы.
Например в классах PrintHead, PrintBody и PrintFooter используется одноименный метод print()
. Если стоит задача добавить какой-то новый вариант, то нужно будет следовать этой же схеме. Но, представим себе, что какой-то программист решил использовать другой метод, например out()
. В этом случае при создании класса Print волей-неволей придётся учитывать эту особенность.
Чтобы избегать таких ситуаций, используют интерфейсы. С их помощью гарантируется единый совместимый тип данных.
interface PrintInterface { public function print(); } class PrintHead implements PrintInterface class PrintFooter implements PrintInterface class PrintBody implements PrintInterface
Агрегация
Теперь рассмотрим «чистую» агрегацию (агрегирование по ссылке). В отличие от композиции, класс не создаёт другие объекты, а получает лишь ссылку на уже готовые экземпляры. В простом примере это может выглядеть так:
class Print { public function go($h, $b, $f) { $h->print(); $b->print(); $f->print(); } } $head = new PrintHead(); $body = new PrintBody(); $footer = new PrintFooter(); $print = new Print(); $print->go($head, $body, $footer); // HEAD BODY FOOTER
После того, как объект Print будет уничтожен, остальные останутся и будут доступны для использования. При агрегации также можно перенести логику выполнения из исполняемого класса Print: скажем поменять местами head и footer в параметрах.