Реализация наследования в РНР

Если класс должен быть субклассом другого класа, для указания этого факта используется ключевое слово extends. Приведенный ниже код создает класс В, унаследованный от ранее определенного класса А.

class B extends A

{

var $attribute2;

function operation2()

{

}

}

Если класс А объявлен следующим образом:

class A

{

var $attribute1;

function operation1()

{

}

}

то все показанные ниже обращения к операциям и атрибутам объекта типа В будут допустимыми:

$b = new B();

$b ->operation1();

$b -> attribute1 = 10;

$b -> operation2();

$b -> attribute2 = 10;

Обратите внимание, что поскольку класс В является расширением класса А, к операции operation1() и атрибуту $attribute можно обращаться, хотя они и были объявлены в классе А. Будучи подклассом класса А, класс В обладает всеми его функциональными возможностями и данными. Кроме того, в классе В объявляются его собственные атрибут и операция.

Важно отметить, что наследование работает только в одном направлении. Подкласс, или дочерний класс, наследует характеристики своего родительского класса, или суперкласса, но родительский класс не получает характеристики своего дочернего класса. Это означает, что две последних строки в следующем коде неверны:

$а = new A() ;

$a -> operationl () ;

$a -> attributel = 10;

$a -> operation2 ( ) ;

$a->attribute2 = 10;

Класс А не имеет ни операции operation2 , ни атрибута attribute2.

Управление видимостью при наследовании с помощью модификаторов private и protected

Модификаторы доступа private и protected могут использоваться для управления наследованием. Если атрибут или метод определен как private, наследоваться он не будет. Если же атрибут или метод определен как protected, он будет наследоваться, однако не будет видим извне класса (подобно элементу private).

Рассмотрим пример:

class A
{
private function operation1()
{
echo "Вызвана операция operation1";
}
protected function operation2()
{
echo "Вызвана операция operation2<br>";
}
public function operation3()
{
echo "Вызвана операция operation3";
}
}
class B extends A
{
function __construct ()
{
$this -> operation1();
$this -> operation2();
$this -> operation3();
}
}
$b = new B;

Этот код создает в классе А по одной операции каждого типа: public, protected и private. Класс В наследуется от класса А. В рамках конструктора класса В предпринимаются попытки обратиться к операциям родительского класса.

Строка

$this -> operation1();

приводит к фатальной ошибке:

Fatal error: Call to private method A::operation1() from context 'B' in D:\svd\phpstroy\test\52.php on line 21

Этот пример показывает, что приватные операции, определенные в родительском классе, нельзя вызывать из дочернего класса.

Если закомментировать эту строчку кода, остальные две будут работать нормально.

В окне веб-браузера это будет выглядеть ТАК.

Функция с модификатором protected наследуется, однако может использоваться только внутри дочернего класса, что, собственно, и сделано. В результате бобавления строки:

$b ->operation2 ();

в конец файла, будет сгенерирована ошибка:

Fatal error: Call to protected method A::operation2() from context '' in D:\svd\phpstroy\test\52.php on line 27

Вместе с тем, операцию operation3() можно вызвать и за пределами класса:

$b ->operation3 ();

Данный вызов возможен, поскольку operation3() объявлена как public.

Перекрытие

Мы рассмотрели субкласс, в котором объявляются новые атрибуты и операции. Допустимо, а иногда и полезно повторно объявлять ранее объявленные операции. Это может делаться с целью присвоения атрибуту в субклассе значения, которое отличется от значения по умолчанию того же атрибута в суперклассе, или для представления той или иной операции в субклассе функциональных возможностей, отличных от функциональных возможностей той же операции в суперклассе. Упомянутый процесс носит название перекрытия.

Рассмотрим, например, класс А со следующим определением:

class A
{
var $attribute = 'значение по умолчанию';
function operation()
{
echo 'Что-то здесь такое <br>';
echo 'Значением $attribute является'. $this ->attribute.'<br>';
}
}

Необходимо изменить значение атрибута $attribute, назначенное по умолчанию, и наделить операции operation() новыми функциональными возможностями. Для этого можно создать показанный ниже класс В, в котором перекрываются атрибут $attribute и операция operation():

class B extends A
{
var $attribute = 'другое значение';
function operation ()
{
echo 'А здесь что-то другое <br>';
echo 'Значением $attribute является'. $this ->attribute.'<br>';
}
}

Объявление класса В не влияет на исходное определение класса А. Рассмотрим следующие две строки кода:

$a= new A();
$a -> operation();

Эти строки создают объект типа А и вызывают его функцию operation().

В окне веб-браузера это будет выглядеть ТАК.

Это доказывает отсутствие влияния класса В на класс А.

Создав объект типа В:

$b = new B();
$b -> operation();

Получим другой вывод.

В окне веб-браузера это будет выглядеть ТАК.

Как объявление новых атрибутов или операций в субклассе не оказывает влияния на суперкласс, так и перекрытие атрибутов или операций в субклассе не оказывают влияния на суперкласс.

Субкласс наследует все атрибуты и операции своего суперкласса, если вы не предусматриваете их замены. Если вы определяете замену, она получает приоритет и перекрывает исходное определение.

Ключевое слово parent позволяет обращаться к исходной версии операции в родительском классе. Например, для вызова А: :operation() внутри класса В используется следующий код:

function operation ()

{
parent::operation();
}

В окне веб-браузера это будет выглядеть ТАК.

Мы получили другой вывод, несмотря на то, что вызывается операция родительского класса. В данном случае РНР использовал значение атрибутов из текущего класса.

Наследование может достигать нескольких уровней в глубину. Можно объявить скажем, класс С, который расширяет класс В и , следовательно, наследует свойства класса В и класса А, родительского по отношению к классу В. В класс С можно также выборочно перекрывать и заменять атрибуты и операции, унаследованные от родительских классов.

Предотвращение наследования и перекрытия с помощью final

В РНР5 было введено ключевое слово final. Когда это слово используется перед объявлением функции, эта функция не может быть перекрыта ни в одном субклассе. Чтобы продемонстрировать это, добавим final в код класса А из предыдущего примера:

class A
{
var $attribute = 'значение по умолчанию';
final function operation()
{
echo 'Что-то здесь такое <br>';
echo 'Значением $attribute является '. $this ->attribute.'<br>';
}
}

В результате перекрывать operation() в классе В уже будет нельзя. Если вы попытаетесь это сделать, то получите ошибку следующего вида:

Fatal error: Cannot override final method A::operation() in D:\svd\phpstroy\test\56.php on line 20

Можно также запретить создавать субклассы на основе заданного класса, поместив ключевое слово final перед определением класса, например:

final class A

{ ... }

При попытке унаследовать класс будет генерироваться ошибка:

Fatal error: Class B may not inherit from final class (A) in D:\svd\phpstroy\test\56.php on line 20

Множественное наследование

Некоторые объектно-ориентированные языки поддерживают множественное наследование, но РНР не делает этого. Это означает, что каждый класс может наследовать характеристики только от одного родительского класса. Количество дочерних классов, имеющих общий родительский класс, не ограничено.

Значение сказанного может быть понятно не сразу. На рисунке показаны три возможных способа наследования для трех классов А, В и С.

РНР не поддерживает множественное наследование

На рисунке слева показано, что класс С унаследован от класса В, который, в свою очередь, унаследован от класса А. Каждый класс имеет не более одного родительского класса, следовательно, это — совершенно допустимое в РНР одиночное наследование.

На рисунке в центре показано, что классы В и С унаследованы от класса А. Каждый класс имеет не более одного родительского класса, следовательно, и это — допустимое одиночное наследование.

Справа показано, что класс С унаследован от обоих классов А и В. В этом случае класс С имеет два родительских класса, следовательно, это — множественное наследование и оно в РНР недопустимо.

назадвверхвперед
Rambler's Top100