PHP: Наследование
Наследование
Наследование - это механизм объектно ориентированного программирования, который позволяет описать новый класс на основе уже существующего (родительского).
Класс, который получается в результате наследования от другого, называется подклассом. Эту связь обычно описывают с помощью терминов «родительский» и «дочерний». Дочерний класс происходит от родительского и наследует его характеристики: свойства и методы. Обычно в подклассе к функциональности родительского класса (который также называют суперклассом) добавляются новые функциональные возможности.
Чтобы создать подкласс, необходимо использовать в объявлении класса ключевое слово extends
, и после него указать имя класса, от которого выполняется наследование:
<?php class Cat { public $age; function __construct($age) { $this->age = $age; } function add_age () { $this->age++; } } // объявляем наследуемый класс class my_Cat extends Cat { // определяем собственный метод подкласса function sleep() { echo '<br>Zzzzz...'; } } $kitty = new my_Cat(10); // вызываем наследуемый метод $kitty->add_age(); // считываем значение наследуемого свойства echo $kitty->age; // вызываем собственный метод подкласса $kitty->sleep(); ?>
Подкласс наследует доступ ко всем методам и свойствам родительского класса, так как они имеют тип public
. Это означает, что для экземпляров класса my_Cat
мы можем вызывать метод add_age()
и обращаться к свойству $age
не смотря на то, что они определены в классе cat
. Также в приведенном примере подкласс не имеет своего конструктора. Если в подклассе не объявлен свой конструктор, то при создании экземпляров подкласса будет автоматически вызываться конструктор суперкласса.
Обратите внимание на то, что в подклассах могут переопределяться свойства и методы. Определяя подкласс, мы гарантируем, что его экземпляр определяется характеристиками сначала дочернего, а затем родительского класса. Чтобы лучше это понять рассмотрим пример:
<?php class Cat { public $age = 5; function foo() { echo "$this->age"; } } class my_Cat extends Cat { public $age = 10; } $kitty = new my_Cat; $kitty->foo(); ?>
При вызове $kitty->foo()
интерпретатор PHP не может найти такой метод в классе my_Cat
, поэтому используется реализация этого метода заданная в классе Cat
. Однако в подклассе определено собственное свойство $age
, поэтому при обращении к нему в методе $kitty->foo()
, интерпретатор PHP находит это свойство в классе my_Cat
и использует его.
Так как мы уже рассмотрели тему про указание типа аргументов, осталось сказать о том, что если в качестве типа указан родительский класс, то все потомки для метода будут так же доступны для использования, посмотрите на следующий пример:
<?php class Cat { function foo(Cat $obj) {} } class my_Cat extends Cat {} $kitty = new Cat; // передаем методу экземпляр класса my_Cat $kitty->foo( new my_Cat ); ?>
Мы можем обращаться с экземпляром класса my_Cat
так, как будто это объект типа Cat
, т.е. мы можем передать объект типа my_Cat
методу foo()
класса Cat
, и все будет работать, как надо.
Оператор parent
На практике подклассам бывает необходимо расширить функциональность методов родительского класса. Расширяя функциональность за счет переопределения методов суперкласса, в подклассах вы сохраняете возможность сначала выполнить программный код родительского класса, а затем добавить код, который реализует дополнительную функциональность. Давайте разберем как это можно сделать.
Чтобы вызвать нужный метод из родительского класса, вам понадобится обратиться к самому этому классу через дескриптор. Для этой цели в PHP предусмотрено ключевое слово parent. Оператор parent позволяет подклассам обращаться к методам (и конструкторам) родительского класса и дополнять их существующую функциональность. Чтобы обратиться к методу в контексте класса, используются символы "::
" (два двоеточия). Синтаксис оператора parent
:
parent::метод_родительского_класа
Эта конструкция вызовет метод, определенный в суперклассе. Вслед за таким вызовом можно поместить свой программный код, который добавит новую функциональность:
<?php class book { public $title; public $price; function __construct($title, $price) { $this->title = $title; $this->price = $price; } } class new_book extends book { public $pages; function __construct($title, $price, $pages) { // вызываем метод-конструктор родительского класса parent::__construct($title, $price); // инициализируем свойство определенное в подклассе $this->pages = $pages; } } $obj = new new_book('азбука', 35, 500); echo "Книга: $obj->title<br> Цена: $obj->price<br> Страниц: $obj->pages"; ?>
Когда в дочернем классе определяется свой конструктор, PHP не вызывает конструктор родительского класса автоматически. Это необходимо сделать вручную в конструкторе подкласса. Подкласс сначала в своем конструкторе вызывает конструктор своего родительского класса, передавая нужные аргументы для инициализации, исполняет его, а затем выполняется код, который реализует дополнительную функциональность, в данном случае инициализирует свойство подкласса.
Ключевое слово parent
можно использовать не только в конструкторах, но и в любом другом методе, функциональность которого вы хотите расширить, достигнуть этого можно, вызвав метод родительского класса:
<?php class Cat { public $name = "Арни"; function getstr() { $str = "Имя кота: {$this->name}."; return $str; } } class my_Cat extends Cat { public $age = 5; function getstr() { $str = parent::getstr(); $str .= "<br>Возраст: {$this->age} лет."; return $str; } } $obj = new my_Cat; echo $obj->getstr(); ?>
Здесь сначала вызывается метод getstr()
из суперкласса, значение которого присваивается переменной, а после этого выполняется остальной код определенный в методе подкласса.
Теперь, когда мы познакомились с основами наследования, можно, наконец, рассмотреть вопрос видимости свойств и методов.
public, protected и private: управление доступом
До этого момента мы явно объявляли все свойства как public
(общедоступные). И такой тип доступа задан по умолчанию для всех методов.
- К public (общедоступным) свойствам и методам, можно получить доступ из любого контекста.
- К protected (защищенным) свойствам и методам можно получить доступ либо из содержащего их класса, либо из его подкласса. Никакому внешнему коду доступ к ним не предоставляется.
- Вы можете сделать данные класса недоступными для вызывающей программы с помощью ключевого слова private (закрытые). К таким свойствам и методам можно получить доступ только из того класса, в котором они объявлены. Даже подклассы данного класса не имеют доступа к таким данным.
public - открытый доступ:
<?php class human { public $age = 5; public function say() { echo "<br>hello"; } } $obj = new human; // доступ из вызывающей программы echo "$obj->age"; // Допустимо $obj->say(); // Допустимо ?>
private - доступ только из методов класса:
<?php class human { private $age = 5; function say() { // внутри класса доступ к закрытым данным есть echo "$this->age"; } } $obj = new human; // напрямую из вызывающей программы доступа к закрытым данным нет echo "$obj->age"; // Ошибка! доступ закрыт! // однако с помощью метода можно выводить закрытые данные $obj->say(); // Допустимо ?>
protected - защищенный доступ:
Модификатор protected
с точки зрения вызывающей программы выглядит точно так же, как и private
: он запрещает доступ к данным объекта извне. Однако в отличие от private
он позволяет обращаться к данным не только из методов своего класса, но также и из методов подкласса.