06. Наследование, интерфейсы и абстрактные классы
Введение
Когда вы начинаете создавать больше классов, часто возникает ситуация: несколько классов выполняют похожие функции. Вместо того, чтобы дублировать код, можно использовать наследование, интерфейсы и абстрактные классы.Эти механизмы позволяют писать чистый, гибкий и расширяемый код.
ООП без наследования — как музыка без мелодии: можно, но скучно.
Наследование
Наследование позволяет одному классу (дочернему) перенять свойства и методы другого (родительского).Пример наследования
<?php
class User {
public $name;public function __construct($name) {
$this->name = $name;
}
public function hello() {
echo 'Привет, ' . $this->name . '!';
}
}
class Admin extends User {
public function ban($user) {
echo 'Пользователь ' . $user . ' заблокирован администратором ' . $this->name . '!';
}
}
$admin = new Admin('Алексей');
$admin->hello();
$admin->ban('Иван');
?>
Вывод: ``` Привет, Алексей! Пользователь Иван заблокирован администратором Алексей! ```
Что здесь происходит
- Admin — это «расширенная версия» User.
- Он наследует все свойства и методы класса User.
- Можно добавлять новые методы (например, ban()).
Переопределение методов
Дочерний класс может изменить (переопределить) метод родителя.
class Moderator extends User {
public function hello() {
echo 'Здравствуйте, модератор ' . $this->name . '!';
}
}$mod = new Moderator('Сергей');
$mod->hello();
Вывод: ``` Здравствуйте, модератор Сергей! ```
Ключевое слово parent
Если вы хотите вызвать метод родителя внутри дочернего класса:
class SuperAdmin extends Admin {
public function hello() {
parent::hello(); // вызывает метод из User
echo ' (высший уровень доступа)';
}
}$sa = new SuperAdmin('Константин');
$sa->hello();
Вывод: ``` Привет, Константин! (высший уровень доступа) ```
Инкапсуляция + наследование
Можно защитить часть данных родителя, используя protected:
class Account {
protected $balance = 0;public function deposit($amount) {
$this->balance += $amount;
}
}
class PremiumAccount extends Account {
public function bonus() {
$this->balance += 100;
}
public function show() {
echo 'Баланс: ' . $this->balance;
}
}
$acc = new PremiumAccount();
$acc->deposit(500);
$acc->bonus();
$acc->show();
Вывод: ``` Баланс: 600 ```
Интерфейсы
Интерфейс — это «контракт», который обязует класс реализовать определённые методы. Он задаёт форму, но не реализацию.Пример интерфейса
<?php
interface Logger {
public function log($msg);
}class FileLogger implements Logger {
public function log($msg) {
file_put_contents('log.txt', date('H:i:s') . ' — ' . $msg . "\n", FILE_APPEND);
}
}
$logger = new FileLogger();
$logger->log('Пользователь вошёл в систему.');
?>
Файл log.txt будет содержать строки: ``` 12:01:45 — Пользователь вошёл в систему. ```
Зачем нужны интерфейсы
- Они задают единый стандарт для всех классов, реализующих логику
- Позволяют писать универсальный код (например, подменять логгер на EmailLogger, не меняя остальной код)
Несколько интерфейсов
Класс может реализовать несколько интерфейсов сразу:
interface A { public function testA(); }
interface B { public function testB(); }class C implements A, B {
public function testA() { echo 'A'; }
public function testB() { echo 'B'; }
}
$obj = new C();
$obj->testA();
$obj->testB();
Вывод: ``` AB ```
Абстрактные классы
Абстрактный класс — это некий «незаконченный» класс, который нельзя создать напрямую, но можно использовать как основу для других.Пример
abstract class Shape {
protected $color;public function __construct($color) {
$this->color = $color;
}
abstract public function area(); // метод без реализации
}
class Circle extends Shape {
private $radius;
public function __construct($color, $radius) {
parent::__construct($color);
$this->radius = $radius;
}
public function area() {
return 3.14 * $this->radius ** 2;
}
}
$circle = new Circle('red', 5);
echo 'Площадь круга: ' . $circle->area();
Вывод: ``` Площадь круга: 78.5 ```
Абстрактный класс vs интерфейс
- Интерфейс — только описание методов, без реализации.
- Абстрактный класс — может содержать и реализованные, и абстрактные методы.
- Класс может наследовать только один абстрактный класс, но реализовать несколько интерфейсов.
Практика: мини-система логирования
Реализуем пример с двумя логгерами — один пишет в файл, другой выводит в консоль.
interface Logger {
public function log($msg);
}class FileLogger implements Logger {
public function log($msg) {
file_put_contents('file_log.txt', date('H:i:s') . ' ' . $msg . "\n", FILE_APPEND);
}
}
class ConsoleLogger implements Logger {
public function log($msg) {
echo '[ЛОГ] ' . $msg . '<br>';
}
}
function process(Logger $logger) {
$logger->log('Начало работы программы.');
$logger->log('Завершение работы программы.');
}
process(new FileLogger());
process(new ConsoleLogger());
Вывод: ``` [ЛОГ] Начало работы программы. [ЛОГ] Завершение работы программы. ```
Итоги урока
Теперь вы знаете:- Что такое наследование и как переопределять методы родителя
- Как создавать интерфейсы и использовать их как контракты
- Чем отличаются абстрактные классы от интерфейсов
- Как использовать parent:: и protected свойства
- Как писать гибкий и расширяемый код с ООП