Наследование в объектно-ориентированном программировании PHP
Этот урок мы посвятим одной из ключевых тем объектно-ориентированного программирования – теме наследования. Благодаря реализации наследования мы можем организовать связь классов по принципу родительский-дочерний и добавлять дополнительную функциональность в разрабатываемые приложения без необходимости дублирования целых блоков кода.
Что такое наследование?
В объектно-ориентированном программировании наследование позволяет нам создать класс, который наследует функциональность и может использовать свойства и методы от другого класса. Это полезно, когда мы хотим создать класс, который расширяет функциональность исходного класса, не нарушая существующий код, который использует исходный класс.
Эту связь обычно описывают с помощью терминов "родительский" и "дочерний". Класс, от которого мы наследуем, называется базовым классом, суперклассом или родительским классом. Класс, который наследует функциональность, называется подклассом или дочерним классом. В наследовании у нас есть родительский класс со своими собственными методами и свойствами, а также дочерний класс (или классы), которые унаследуют все общедоступные и защищенные свойства и методы родительского класса. Кроме того, у них могут быть свои свойства и методы.
Используя наследование, мы можем создать повторно используемый фрагмент кода, который мы пишем только один раз в родительском классе и используем снова столько, сколько нам нужно в дочерних классах.
Как наследовать от другого класса?
Существующий класс готов к наследованию, нам не нужно делать с ним ничего особенного. Этот класс называется базовым классом, суперклассом или родительским классом.
В PHP мы используем ключевое слово extends
, чтобы указать, что класс наследуется от другого класса.
Синтаксис
<?php class ParentClass { } class ChildClass extends ParentClass { } ?>
В приведенном ниже примере класс SportsCar
наследует класс Car
, поэтому у него есть доступ ко всем методам и свойствам Car
, которые не являются приватными. Это позволяет нам писать общедоступные методы setModel()
и hello()
только один раз в родительском классе, а затем использовать эти методы как в родительском, так и в дочернем классах:
Пример
Попробуй сам »<?php
//Родительский класс
class Car {
// Приватное свойство внутри класса
private $model;
//Публичный метод установки
public function setModel($model) {
$this -> model = $model;
}
public function hello() {
return "Бип! Я <i>" . $this -> model . "</i><br />";
}
}
//Дочерний класс наследует код родительского класса
class SportsCar extends Car {
//Нет кода в дочернем классе
}
//Создаем экземпляр из дочернего класса
$sportsCar1 = new SportsCar();
// Устанавливаем значение свойства класса
// Для этого мы используем метод, который мы создали в родительском
$sportsCar1 -> setModel('Mercedes Benz');
//Используем второй метод, который дочерний класс унаследовал от родительского класса
echo $sportsCar1 -> hello();
?>
Результат выполнения кода:
Собственные методы и свойства дочернего класса
Так же, как дочерний класс может использовать свойства и методы своего родительского класса, он также может иметь собственные свойства и методы. Однако, хотя дочерний класс может использовать код, унаследованный от родительского, родительскому классу не разрешается использовать код дочернего класса.
В приведенном ниже примере мы добавим в дочерний класс некоторый собственный код, добавив свойство $style
, а также метод driveItWithStyle()
:
Пример
Попробуй сам »<?php
class Car {
//Приватное свойство или метод может использовать только родитель
private $model;
// Публичные методы и свойства могут использоваться как родительским, так и дочерним классами
public function setModel($model) {
$this -> model = $model;
}
public function getModel() {
return $this -> model;
}
}
// Дочерний класс может использовать код, унаследованный от родительского класса,
// а также может иметь собственный код
class SportsCar extends Car {
private $style = 'быстрый и надёжный';
public function driveItWithStyle() {
return 'Автомобиль ' . $this -> getModel() . ' ' . $this -> style . '';
}
}
//создать экземпляр из дочернего класса
$sportsCar1 = new SportsCar();
// Используем метод, который дочерний класс унаследовал от родительского класса
$sportsCar1 -> setModel('Ferrari');
// Используем метод, который был добавлен в дочерний класс
echo $sportsCar1 -> driveItWithStyle();
?>
Результат выполнения кода:
Наследование и модификатор защищенного доступа
В предыдущем уроке мы узнали, что можем использовать модификатор публичного доступа, чтобы разрешить доступ к методам и свойствам класса как внутри, так и за его пределами. Мы также узнали, что те методы и свойства, которые являются приватными, могут использоваться только внутри класса.
В этом уроке мы узнаем о третьем модификаторе — protected
, который позволяет использовать код как внутри класса, так и из его дочерних классов.
Как вы думаете, что может случиться, когда мы попытаемся вызвать приватный метод или свойство извне класса?
Следующий пример демонстрирует, что может произойти, если мы объявляем свойство $model
в родительском элементе приватным, но все же пытаемся получить к нему доступ из его дочернего класса:
Пример
Попробуй сам »<?php
class Car {
// Свойство $model является приватным, поэтому к нему можно получить доступ
// только изнутри класса
private $model;
//Публичный метод установки
public function setModel($model) {
$this -> model = $model;
}
}
// Дочерний класс
class SportsCar extends Car {
//Пытаемся получить доступ к приватному свойству model
public function hello() {
return "Бип! Я <i>" . $this -> model . "</i><br />";
}
}
//Создаём экземпляр из дочернего класса
$sportsCar1 = new SportsCar();
//Устанавливаем имя модели класса
$sportsCar1 -> setModel('Mercedes Benz');
//Получаем значение свойства model
echo $sportsCar1 -> hello();
?>
Результат выполнения кода:
Мы не вывели значение модели автомобиля $model
, потому что метод hello()
в дочернем классе пытается получить доступ к приватному свойству $model
, которое принадлежит родительскому классу.
Мы можем решить эту проблему, объявив свойство $model
в родительском классе как защищенное protected
, а не приватное, потому что, когда мы объявляем свойство или метод как защищенные, мы можем обращаться к нему как из родительского, так и из дочернего классов:
Пример
Попробуй сам »<?php
class Car {
// Свойство $model теперь защищено, поэтому к нему можно получить доступ
// из класса и его дочерних классов
protected $model;
public function setModel($model) {
$this -> model = $model;
}
}
class SportsCar extends Car {
// Теперь есть доступ к защищенному свойству, принадлежащему родителю
public function hello() {
return "Бип! Я <i>" . $this -> model . "</i><br />";
}
}
//Создаём экземпляр из дочернего класса
$sportsCar1 = new SportsCar();
//Задаём имя модели класса
$sportsCar1 -> setModel('Mercedes Benz');
//Получаем имя модели класса
echo $sportsCar1 -> hello();
?>
Результат выполнения кода:
Сказанное выше относится и к методам:
Пример
Попробуй сам »<?php
class Car {
public $name;
public $color;
public function __construct($name, $color) {
$this->name = $name;
$this->color = $color;
}
protected function intro() {
echo "Автомобиль {$this->name} имеет цвет: {$this->color}.";
}
}
class SportsCar extends Car {
public function message() {
echo "Какой цвет у автомобиля? " . "<br>";
// доступ к защищенному методу, принадлежащему родителю
$this -> intro();
}
}
//Создаём экземпляр из дочернего класса
$sportsCar1 = new SportsCar('Mercedes Benz', 'Красный');
$sportsCar1 -> message();
?>
Результат выполнения кода:
Автомобиль Mercedes Benz имеет цвет: Красный.
Публичный метод message() дочернего класса SportsCar имеет доступ к методу intro() (который защищен) родительского класса.
Переопределение родительских свойств и методов
Так же, как дочерний класс может иметь свои собственные свойства и методы, он может переопределять свойства и методы родительского класса. Когда мы переопределяем свойства и методы класса, мы переписываем метод или свойство (с использованием того же имени), которое существует в родительском элементе, снова в дочернем, но присваиваем ему другое значение или код.
Посмотрите на пример ниже. Методы __construct() и intro() в дочернем классе (SportsCar) переопределят методы __construct() и intro() в родительском классе (Car):
Пример
Попробуй сам »<?php
class Car {
public $name;
public $color;
public function __construct($name, $color) {
$this->name = $name;
$this->color = $color;
}
public function intro() {
echo "Автомобиль {$this->name} имеет цвет: {$this->color}.";
}
}
class SportsCar extends Car {
public $weight;
public function __construct($name, $color, $weight) {
$this->name = $name;
$this->color = $color;
$this->weight = $weight;
}
public function intro() {
echo "Автомобиль {$this->name} имеет цвет {$this->color}, а его вес {$this->weight} кг.";
}
}
$sportsCar1 = new SportsCar('Mercedes Benz', 'Красный', 1800);
$sportsCar1 -> intro();
?>
Результат выполнения кода:
Ключевое слово final
Ключевое слово final
может быть использовано для предотвращения наследования класса или для предотвращения переопределения метода.
В приведенном ниже примере мы объявляем класс Car как final, чтобы предотвратить наследование класса, но все же пытаемся его наследовать. В результате мы получим ошибку:
Пример
Попробуй сам »<?php
final class Car {
// какой-то код
}
// приведет к ошибке
class SportsCar extends Car {
// какой-то код
}
?>
Результат выполнения кода:
В следующем примере ключевое слово final
испольуется для предотвращения переопределение метода:
Пример
Попробуй сам »<?php
class Car {
final public function intro() {
// какой-то код
}
}
class SportsCar extends Car {
public function intro() { // приведет к ошибке
// какой-то код
}
}
?>
Результат выполнения кода:
Попытка переопределить родительский метод intro(), защищенный ключевым словом final
, привела к ошибке.
Заключение
Мы используем наследование, чтобы уменьшить дублирование кода за счет использования кода из родительского класса в дочерних классах. В этом уроке мы изучили один из принципов объектно-ориентированного программирования — концепцию наследования. Мы используем наследование, чтобы уменьшить дублирование кода, используя код родительского класса в дочерних классах. Щелкните здесь, чтобы попрактиковаться в предмете.