Оператор instanceof
Когда у вас несколько классов в одной иерархии, возникает вопрос: как во время выполнения программы узнать, к какому именно типу принадлежит объект? Для этого существует оператор instanceof. Он возвращает true, если объект является экземпляром указанного класса или любого его потомка.
Важный нюанс: instanceof учитывает всю цепочку. Объект дочернего класса одновременно является экземпляром родительского — это называется принципом подстановки Лисков.
$email = new EmailNotification('a@b.ru', 'Привет');
$urgent = new UrgentNotification('a@b.ru', 'Срочно!');
var_dump($email instanceof EmailNotification); // true
var_dump($email instanceof Notification); // true — это тоже Notification
var_dump($urgent instanceof EmailNotification); // false — другая ветка
// Практическое применение: разная обработка по типу
function dispatch(Notification $n): void
{
if ($n instanceof UrgentNotification) {
echo "Немедленно отправляем: " . $n->format() . "\n";
return;
}
echo "Стандартная очередь: " . $n->format() . "\n";
}
dispatch($email); // Стандартная очередь: [a@b.ru] Привет
dispatch($urgent); // Немедленно отправляем: [СРОЧНО] Срочно!
instanceof работает не только с классами, но и с интерфейсами. Если объект реализует интерфейс, проверка $obj instanceof SomeInterface вернёт true. Это делает оператор универсальным инструментом для проверки типов.
Запрет наследования: оператор final
Иногда нужно защитить класс или отдельный метод от переопределения. PHP предоставляет для этого ключевое слово final. Оно применяется в двух сценариях.
final перед классом
Класс, объявленный как final, не может иметь потомков вообще. Любая попытка унаследоваться от него вызовет фатальную ошибку. Это используется, когда класс представляет законченную, неделимую сущность, расширение которой нарушило бы логику программы.
final class HashedPassword
{
private string $hash;
public function __construct(string $plainText)
{
$this->hash = password_hash($plainText, PASSWORD_BCRYPT);
}
public function verify(string $plainText): bool
{
return password_verify($plainText, $this->hash);
}
}
// Это вызовет фатальную ошибку:
// class WeakPassword extends HashedPassword {}
// Fatal error: Class WeakPassword cannot extend final class HashedPassword
final перед методом
Можно запретить переопределение только одного конкретного метода, оставив сам класс открытым для наследования. Это тонкий инструмент: класс расширяется, но определённая часть его поведения остаётся неизменной.
class Notification
{
public function __construct(
protected string $recipient,
protected string $body
) {}
// Этот метод нельзя переопределить ни в одном потомке
final public function getRecipient(): string
{
return $this->recipient;
}
// Этот метод можно переопределять
public function format(): string
{
return "[{$this->recipient}] {$this->body}";
}
}
class EmailNotification extends Notification
{
// format() — можно, он не final
public function format(): string
{
return "Email: " . parent::format();
}
// getRecipient() — нельзя, он final
// public function getRecipient(): string { ... }
// Fatal error: Cannot override final method Notification::getRecipient()
}
Не злоупотребляйте final. Излишнее его применение затрудняет тестирование: финальные классы нельзя мокировать стандартными инструментами. Помечайте как final только то, что действительно не должно меняться по архитектурным причинам.
Наследование в PHP — не просто способ «скопировать» код из одного класса в другой. Это инструмент выражения отношений между сущностями: дочерний класс является частным случаем родительского, уточняет его или расширяет.
Оператор final закрывает наследование там, где это необходимо по архитектурным соображениям — будь то весь класс или отдельный метод. Вместе эти инструменты дают полный контроль над тем, как классы в вашем приложении соотносятся друг с другом.
0 Комментарий(я)
Зарегистрируйтесь чтобы оставить комментарий