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 он позволяет обращаться к данным не только из методов своего класса, но также и из методов подкласса.