PHP - Абстрактные классы и методы
Некоторые методы обретают смысл только тогда, когда дочерний класс дополнит детали. Именно таков абстрактный метод: родитель объявляет его, но не даёт реализации, оставляя эту работу дочернему классу или классам.
Класс хотя бы с одним абстрактным методом называется абстрактным классом. Его цель — заставить соблюдать правило: каждый производный класс (дочерний класс) обязан реализовать абстрактные методы, объявленные родителем.
И абстрактные классы, и абстрактные методы помечаются ключевым словом abstract.
Вот как это выглядит на практике:
Пример
Car является абстрактным классом и объявляет абстрактный метод intro(), который реализует каждый из двух его дочерних классов:
Вывод: PHP-скрипт
<?php
// Абстрактный базовый класс
abstract class Car {
public $name;
// Неабстрактный метод
public function __construct($name) {
$this->name = $name;
}
// Абстрактный метод - заставляет дочерние классы реализовать его
abstract public function intro();
}
// Дочерний класс, который расширяет абстрактный класс
class Audi extends Car {
public function intro() {
return "Немецкое качество! Я — $this->name!";
}
}
// Дочерний класс, который расширяет абстрактный класс
class Citroen extends Car {
public function intro() {
return "Французская экстравагантность! Я — $this->name!";
}
}
// Создаём объекты дочерних классов
$audi = new audi("Audi");
echo $audi->intro();
echo "<br>";
$citroen = new citroen("Citroen");
echo $citroen->intro();
?>
Разбор примера
Audi и Citroen расширяют абстрактный класс Car. Благодаря этому наследованию они могут использовать его публичное свойство $name и его публичный метод __construct().
С intro() всё иначе. Он абстрактный, поэтому каждый дочерний класс обязан предоставить собственную реализацию.
Замечания об абстрактных классах
Реализуя абстрактный метод, дочерний класс должен соблюдать несколько правил. Метод должен сохранить то же имя, а его модификатор доступа должен быть таким же или менее строгим. Например, абстрактный метод, объявленный как protected, может остаться protected или стать public в дочернем классе, но никогда не может стать private. Тип и количество обязательных аргументов также должны совпадать, хотя дочерний класс может свободно добавлять необязательные аргументы.
Итак, когда дочерний класс наследует абстрактный класс, действуют следующие правила:
- Метод в дочернем классе имеет то же имя, что и абстрактный метод, и повторно объявляет его
- Его модификатор доступа такой же, как у родителя, или менее строгий
- Он принимает то же количество обязательных аргументов и может добавлять необязательные
PHP - Абстрактный метод с аргументом
Абстрактные методы тоже могут принимать аргументы. Как это работает, показывают следующие два примера:
Пример
Абстрактный метод объявляет один аргумент, $name, а дочерний класс реализует его с тем же аргументом:
Вывод: PHP-скрипт
<?php
abstract class ParentClass {
// Абстрактный метод с аргументом
abstract protected function prefixName($name);
}
class ChildClass extends ParentClass {
public function prefixName($name) {
if ($name == "John Doe") {
$prefix = "Mr.";
} elseif ($name == "Jane Doe") {
$prefix = "Mrs.";
} else {
$prefix = "";
}
return "$prefix $name";
}
}
$class = new ChildClass;
echo $class->prefixName("John Doe");
echo "<br>";
echo $class->prefixName("Jane Doe");
echo "<br>";
echo $class->prefixName("Baby Doe");
?>
Пример
Здесь дочерний класс добавляет два необязательных аргумента, которые не объявлены в абстрактном методе родителя:
Вывод: PHP-скрипт
<?php
abstract class ParentClass {
// Абстрактный метод с аргументом
abstract protected function prefixName($name);
}
class ChildClass extends ParentClass {
// Дочерний класс может объявлять необязательные аргументы, которых нет в абстрактном методе родителя
public function prefixName($name, $separator = ".", $greet = "Dear") {
if ($name == "John Doe") {
$prefix = "Mr";
} elseif ($name == "Jane Doe") {
$prefix = "Mrs";
} else {
$prefix = "";
}
return "$greet $prefix$separator $name";
}
}
$class = new ChildClass;
echo $class->prefixName("John Doe");
echo "<br>";
echo $class->prefixName("Jane Doe");
?>