В курсе "PHP: Полиморфизм" мы разбирали как работает полиморфизм изнутри. Но тогда мы ещё не знали про наследование достаточно, чтобы раскрыть этот вопрос полностью. Сейчас пришло время узнать об этом.
Перед тем как начать, нужно вспомнить, что для полиморфизма наследование не нужно. С другой стороны, наследование участвует в процессе выбора метода и об этом тоже нужно знать. Посмотрите на код, который был в курсе, посвящённому полиморфизму:
<?php
// Что происходит во время вызова: $obj->getName() => callMethod($obj, 'getName')
function callMethod($data, $methodName, $args) // функция-диспетчер
{
$className = $data['className'];
// Специальная функция, которая хранит список классов и связанных с ними методов
$methods = getClassMethods($className);
// Берём нужный метод и вызываем его
$method = $methods[$methodName];
if ($method) {
$method($data, ...$args);
} else if (!$method && isset($methods['__call'])) {
// Если метод $method не найден, но определён метод __call, то вызываем его
$methods['__call']($data, ...$args);
}
throw new \Exception('No method error');
}
В этом коде проверка останавливается, если ничего не было найдено. Так было бы без наследования, но с наследованием поиск продолжается. Сначала выбирается базовый класс для текущего и метод ищется там. Если он не найден, то снова проверяется наличие __call()
. Затем процесс повторяется для родительского класса родителя текущего класса и далее до конца цепочки наследования.
Из этого, кстати, следует интересный вывод. Метод __call
это "тяжелый" вызов. Он требует больше вычислений для поиска. И чем ближе к началу иерархии расположен __call()
тем хуже с точки зрения производительности.
Позднее связывание в сравнении с динамической диспетчеризацией
Программисты часто путают позднее связывание и динамическую диспетчеризацию. Ситуация усугубляется тем, что в некоторых языках (например, Java) принято на уровне документации и сообщества подменять одно понятие другим.
Связывание говорит нам о том, чем является идентификатор, какой у него тип. Если связывание раннее, то мы это знаем сразу, в случае позднего, только в момент вызова кода. Во многих языках можно определить функцию после того, как где-то она используется. Это тоже пример связывания (позднего). В случае $this
в PHP, мы знаем что это объект текущей иерархии, но не знаем точно какой класс до момента срабатывания этого кода.
Диспетчеризация же, это процесс поиска и вызова необходимой функции (или метода, в зависимости от языка) для уже известного типа данных.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.