Полиморфизм в PHP
В этом уроке мы поговорим о последнем из трёх китов ООП – полиморфизме. В этом уроке нам придется отойти от примеров с котиками и собачками, и немного удариться в абстракции.
Однако перед этим давайте вспомним конструкцию instanceof. Она позволяет узнать, является ли объект экземпляром какого-то класса, либо что он реализует какой-либо интерфейс. Возвращает true или false.
class A
{
public function sayHello()
{
return 'Hello, I am A';
}
}
$a = new A();
var_dump($a instanceof A); // true
Давайте создадим ещё один класс, который будет наследником класса A, и выполним ту же проверку для объекта этого класса.
class B extends A
{
}
$b = new B();
var_dump($b instanceof B); // true
При этом если проверить объект дочернего класса, является ли он объектом родительского класса, то мы получим true.
class B extends A
{
}
$b = new B();
var_dump($b instanceof A); // тоже true!
Это правило работает только в одну сторону, объекты родительского класса не будут являться экземплярами дочернего.
class A
{
public function sayHello()
{
return 'Hello, I am A';
}
}
class B extends A
{
}
$a = new A();
$b = new B();
var_dump($a instanceof B); // false
Согласитесь, это вполне логично.
Таким образом объекты дочерних классов будут проходить проверку на то, что они являются экземплярами родительских классов.
Как мы помним, методы объектов родительских классов у нас доступны и в дочерних – они наследуются. Соответственно мы можем быть уверены, что эти же методы есть и у дочерних объектов. Конкретно в нашем примере – у объектов класса B будет метод sayHello(), унаследованный от A.
Или в примере с интерфейсами из прошлого урока – мы определили, что объекты, реализовавшие какой-то интерфейс обязательно будут иметь метод с определённым набором параметров. То есть мы можем рассчитывать на то, что этот метод у объекта гарантированно есть и мы можем его вызвать.
Благодаря этому свойству объектов мы можем ожидать от них какого-то определенного поведения, если они являются объектами какого-то класса или реализуют какой-то интерфейс.
Это свойство, благодаря которому мы можем зависеть не от конкретной реализации (проще говоря конкретного класса), а от абстракции (интерфейса), и есть – полиморфизм.
Да-да, в примере про вычисление площадей фигур мы уже сталкивались с полиморфизмом! В тот момент, когда мы проверяли, имеется ли у объекта интерфейс для вычисления площади, мы работали с проявлением полиморфизма.
Переопределение методов
Как мы уже сказали, в дочерних объектах доступны методы объектов родительского класса.
class A
{
public function sayHello()
{
return 'Hello, I am A';
}
}
class B extends A
{
}
$b = new B();
echo $b->sayHello(); // Hello, I am A
Однако, мы можем переопределить этот метод в классе B. Для этого мы описываем метод с таким же названием и описываем в нём свою логику:
class A
{
public function sayHello()
{
return 'Hello, I am A';
}
}
class B extends A
{
public function sayHello()
{
return 'Hello, I am B';
}
}
$b = new B();
echo $b->sayHello(); // 'Hello, I am B
Мы также можем вызвать родительский метод в дочернем при помощи слова parent и двойного двоеточия.
<?php
class A
{
public function sayHello()
{
return 'Hello, I am A';
}
}
class B extends A
{
public function sayHello()
{
return parent::sayHello() . '. It was joke, I am B :)';
}
}
$b = new B();
echo $b->sayHello(); // Hello, I am A. It was joke, I am B :)
Мы вызвали родительский метод, и дополнили его функционал. Так часто приходится делать в реальных проектах. Буквально через пару уроков вы столкнетесь с более понятными примерами того, для чего всё это нужно. Но пока что мы должны изучить базу на немного искусственных примерах. К сожалению, придумать что-то более интересное я не смог.
И ещё один примерчик, тоже искуственный.
class A
{
public function method1()
{
return $this->method2();
}
protected function method2()
{
return 'A';
}
}
class B extends A
{
protected function method2()
{
return 'B';
}
}
$b = new B();
echo $b->method1();
Как думаете, что выведет этот код?
A или B?
Не знаю, что вы ответили, но он в любом случае выведет B. Внутри метода method1() будет вызван тот метод method2(), который определён для класса, в котором его выполняют. То есть $this не привязан напрямую к классу A. Когда мы вызываем method1() у экземпляра класса B, то $this внутри него будет связан с классом B, и вызовется метод method2(), определенный именно в классе B.
И это – тоже полиморфизм. $this привязывается к объекту, в котором код выполняется, а не там, где он был описан.
На этом с полиморфизмом пока всё, если что-то непонятно – пишите в комментах.
Комментарии