Подсказка типов данных для интерфейсов в PHP
В первой части этого урока мы увидим, что использования подсказки типов данных для объектов не всегда достаточно для рабрты с кодом, а во второй части мы узнаем, как решить эту проблему, используя подсказку типов данных для интерфейсов.
Почему подсказки типов для объектов может быть недостаточно?
Давайте Напишем следующий сценарий: менеджер компании по аренде автомобилей, которая арендует только BMW, нанял программиста, чтобы он написал программу, которая рассчитывает цену за полный бак бензина для каждого автомобиля, которым владеет компания. В соответствии с этими требованиями программист пишет класс Bmw, который содержит код задачи. Этот класс содержит данные о BMW, включая номерной знак, модель автомобиля и, что наиболее важно, метод, который вычисляет объем топливного бака.
Функция, вычисляющая объем, называется calcTankVolume
. Этот метод рассчитывает объем топливного бака путем умножения площади основания (квадрата длины ребер основания) на высоту. Высота, а также длина ребер основания и номерной знак вводятся в класс через конструктор:
Пример
<?php
class Bmw {
protected $model;
protected $rib;
protected $height;
// Свойства вводятся в класс через конструктор
public function __construct($model, $rib, $height)
{
$this -> model = $model;
$this -> rib = $rib;
$this -> height = $height;
}
// Рассчитать объем бак для прямоугольных резервуаров
public function calcTankVolume()
{
return $this -> rib * $this -> rib * $this -> height;
}
}
?>
Вне занятий программист пишет функцию для расчета цены за полный бак бензина путем умножения объема бака (литров) на цену за литр. Однако, поскольку он не хочет, чтобы функция получала какие-либо аргументы, кроме тех, которые принадлежат классу Bmw
, он использует подсказку типов:
Пример
<?php
// Подсказка типа гарантирует, что функция получит только объекты Bmw в качестве аргументов
function calcTankPrice(Bmw $bmw, $pricePerLitr)
{
return $bmw -> calcTankVolume() * $pricePerLitr . "$";
}
?>
Теперь он может легко подсчитать, сколько стоит полный бак бензина для BMW. Например, для автомобиля с номерным знаком 777 длина ребра 3.5 дм на высоту 5 дм и использование бензина по цене 1 доллар за литр:
Пример
<?php
$bmw1 = new Bmw('777', 3.5, 5);
echo calcTankPrice($bmw1, 1);
?>
Результат: 61,25$
Как делать подсказки типов для интерфейсов?
Через короткое время менеджер решает ввести в свой парк автомобилей новый Mercedes, но проблема в том, что функция calcTankPrice()
может выполнять вычисления только для BMW. Оказывается, если у BMW бензобак прямоугольной формы, то у Mercedes — бензобак цилиндрической формы. Итак, наш программист снова призван написать другой класс, который сможет справиться с этой новой задачей. С этой целью наш программист теперь пишет следующий класс с именем Mercedes, который может вычислять объем бака для баков цилиндрической формы:
Пример
<?php
class Mercedes {
protected $model;
protected $radius;
protected $height;
public function __construct($model, $radius, $height)
{
$this -> model = $model;
$this -> radius = $radius;
$this -> height = $height;
}
// Рассчитывает объем цилиндра
public function calcTankVolume()
{
return $this -> radius * $this -> radius * pi() * $this -> height;
}
}
?>
Когда наш программист выполнил задачу по написанию класса Mercedes
, он попытался посчитать стоимость полного бака бензина для Mercedes
:
Пример
<?php
$mercedes1 = new Mercedes('555', 1.8, 7);
echo calcTankPrice($mercedes1, 1);
?>
Однако результат оказался не совсем таким, как он ожидал:
Результат выполнения кода:
Это сообщение об ошибке является результатом того, что в функцию не был передан правильный объект, поскольку он пытался передать объект Mercedes
в функцию calcTankVolume()
, тогда как функция может принимать только объекты, принадлежащие классу Bmw
.
Сначала наш программист попытался решить проблему, не используя подсказки типов, но затем он понял, что лучшим решением будет использование подсказок типов для интерфейсов. Здесь мы используем интерфейс в его более широком смысле, который включает как абстрактные классы, так и реальные интерфейсы .
Итак, сначала он создал абстрактный класс с именем Car
, от которого могут унаследоваться как Bmw
, так и Mercedes
(и, фактически, любая другая модель автомобиля):
Пример
<?php
abstract class Car {
protected $model;
protected $height;
abstract public function calcTankVolume();
}
?>
Затем он реорганизовал классы Bmw
и Mercedes
, чтобы они унаследовали от класса Car
:
Пример
<?php
class Bmw extends Car {
protected $rib;
public function __construct($model, $rib, $height)
{
$this -> model = $model;
$this -> rib = $rib;
$this -> height = $height;
}
// Расчет объема прямоугольного бака
public function calcTankVolume()
{
return $this -> rib * $this -> rib * $this -> height;
}
}
class Mercedes extends Car {
protected $radius;
public function __construct($model, $radius, $height)
{
$this ->model = $model;
$this -> radius = $radius;
$this -> height = $height;
}
// Рассчитывает объем цилиндров
public function calcTankVolume()
{
return $this -> radius * $this -> radius * pi() * $this -> height;
}
}
?>
Следующее изображение может помочь нам лучше понять связь между интерфейсом Car
и конкретными классами ( Bmw
и Mercedes
), реализующими интерфейс для различных форм бензобаков:
Поскольку оба класса наследуются от одного и того же интерфейса, можно удобно ввести подсказку функции calcTankPrice()
с интерфейсом Car
, чтобы функция могла получить любой объект, если он принадлежит этому интерфейсу:
Пример
<?php
// Подсказка типа гарантирует, что функция получит только объекты
// принадлежащие интерфейсу Car
function calcTankPrice(Car $car, $pricePerLitr)
{
echo $car -> calcTankVolume() * $pricePerLitr . "$";
}
?>
Теперь посмотрим на результат, когда мы попытаемся использовать функцию calcTankPrice()
как для объектов Bmw
, так и для Mercedes
:
Пример
<?php
$bmw1 = new Bmw('777', 3.5, 5);
echo calcTankPrice($bmw1, 1);
$mercedes1 = new Mercedes('555', 1.8, 7);
echo calcTankPrice($mercedes1, 1);
?>
Результат выполнения кода:
71.251321383417$
Примечание: Всякий раз, когда нам нужно сделать подсказку типа для нескольких связанных классов, мы должны использовать подсказку типа интерфейса.
Очень важный урок, который можно извлечь из приведенных выше примеров, — это важность программирования интерфейса. Когда программист сделал функцию calcTankPrice
зависимой от класса Bmw, он ограничил использование класса только одним типом автомобиля. Но когда он решил сделать функцию зависимой от интерфейса, он разрешил использовать функцию для любого типа автомобиля и, таким образом, сделал ее более гибкой. Из этого можно сделать вывод, что программирование интерфейса делает код более гибким и готовым к изменениям.
Пример
Попробуй сам »<?php
abstract class Car {
protected $model;
protected $height;
abstract public function calcTankVolume();
}
// Подсказка типа гарантирует, что функция получит только объекты
// принадлежащие интерфейсу Car
function calcTankPrice(Car $car, $pricePerLitr)
{
echo $car -> calcTankVolume() * $pricePerLitr . "$";
}
class Bmw extends Car {
protected $rib;
public function __construct($model, $rib, $height)
{
$this -> model = $model;
$this -> rib = $rib;
$this -> height = $height;
}
// Расчет объема прямоугольного бака
public function calcTankVolume()
{
return $this -> rib * $this -> rib * $this -> height;
}
}
class Mercedes extends Car {
protected $radius;
public function __construct($model, $radius, $height)
{
$this ->model = $model;
$this -> radius = $radius;
$this -> height = $height;
}
// Рассчитывает объем цилиндров
public function calcTankVolume()
{
return $this -> radius * $this -> radius * pi() * $this -> height;
}
}
$bmw1 = new Bmw('777', 3.5, 5);
echo calcTankPrice($bmw1, 1);
echo "<br>";
$mercedes1 = new Mercedes('555', 1.8, 7);
echo calcTankPrice($mercedes1, 1);
?>
Результат выполнения кода:
BMW : 533
Заключение
В этом руководстве вы узнали, как реализовать подсказку типов для интерфейсов в PHP. Кликните здесь, чтобы попрактиковаться в предмете. Об использование подсказок типов для интерфейсов будет объяснено в следующем уроке.
В этом уроке вы узнали, как реализовать подсказку типов для интерфейсов в PHP. Кликните здесь, чтобы попрактиковаться в предмете.