Объектно-ориентированный подход в PHP
В этом уроке мы подошли к той теме, ради которой были сделаны все предыдущие уроки этого курса. Мы изучили основные возможности объектно-ориентированного программирования в PHP, а сейчас мы поговорим о том, что же нам реально позволяет делать объектно-ориентированный подход в программировании на PHP.
Вся суть объектно-ориентированного программирования – это про взаимодействие одних объектов с другими объектами. Сама эта концепция, как не трудно догадаться, пришла к нам из реальной жизни – ведь здесь происходит ровно то же самое.
Мы создаём объекты, добавляем в них свойства и методы. Всё что с модификатором public – это интерфейс, через который мы можем взаимодействовать с объектом. Через этот интерфейс мы можем изменять внутреннее состояние объекта, добиться от него каких-то действий, или получить от него что-то. Аналогично и в жизни – у человека, например, есть органы чувств – это тоже интерфейс. Сказав что-то человеку в его ухо, можно неплохо так изменить его внутреннее состояние. Можно помахать человеку рукой, в интерфейс «глаз». В ответ он может return-уть в ответ тем же, а может и нет, зависит от его внутреннего состояния.
Как вы понимаете, возможность разделить логику программы по отдельным классам – это большой плюс. Это позволяет разделить зоны ответственности, каждый класс содержит свою логику, и вы будете знать, что класс Article – содержит в себе функционал, который отвечает за статьи на сайте, а класс User – за пользователей. И если вам что-то нужно будет изменить, вы будете знать где это искать. К примеру, нужно добавить для статей возможность за них голосовать. Вы будете добавлять этот функционал в классе Article, а не где-то ещё.
Давайте покажем пример взаимодействия двух объектов – статья и пользователь. Предположим, у статьи должны быть заголовок и текст, а ещё у неё обязательно должен быть автор. Тогда наш код будет выглядеть следующим образом:
class User
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
class Article
{
private $title;
private $text;
private $author;
public function __construct(string $title, string $text, User $author)
{
$this->title = $title;
$this->text = $text;
$this->author = $author;
}
public function getTitle(): string
{
return $this->title;
}
public function getText(): string
{
return $this->text;
}
public function getAuthor(): User
{
return $this->author;
}
}
Заметили? В конструктор мы передаём объект класса User, а не что-либо ещё. Это тайп-хинтинг – мы про него уже говорили. Он позволяет указывать в аргументах типы передаваемых переменных, однако до этого мы использовали только скалярные типы – строки, числа. Но в PHP можно указывать в качестве типов и объекты!
Давайте проверим, что всё работает. Создадим пользователя и создадим новую публикацию от его имени.
$author = new User('Иван');
$article = new Article('Заголовок', 'Текст', $author);
var_dump($article);
Все успешно отработало и мы получили следующий вывод:
object(Article)[2]
private 'title' => string 'Заголовок' (length=18)
private 'text' => string 'Текст' (length=10)
private 'author' =>
object(User)[1]
private 'name' => string 'Иван' (length=8)
Видим объект класса Article, у которого в свойстве author лежит объект класса User.
Теперь, если мы захотим получить имя автора статьи мы сможем сделать это следующим образом:
$author = new User('Иван');
$article = new Article('Заголовок', 'Текст', $author);
echo 'Имя автора: ' . $article->getAuthor()->getName();
Результат:
Имя автора: Иван
Как это работает? Да всё очень просто. Мы с помощью метода getAuthor() получили от статьи объект с типом User, а далее сразу же у этого объекта вызвали метод getName() и получили строковое значение поля name. Вот это и есть объектно-ориентированный подход – взаимодействие между объектами.
Ещё про тайп-хинтинг
Благодаря тому, что мы можем проверять типы передаваемых переменных, мы можем гарантировать, что автором статьи, например, не является кот. Давайте попробуем такое провернуть.
<?php
class User
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
class Cat
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
class Article
{
private $title;
private $text;
private $author;
public function __construct(string $title, string $text, User $author)
{
$this->title = $title;
$this->text = $text;
$this->author = $author;
}
public function getTitle(): string
{
return $this->title;
}
public function getText(): string
{
return $this->text;
}
public function getAuthor(): User
{
return $this->author;
}
}
$author = new User('Иван');
$cat = new Cat('Барсик');
$article = new Article('Заголовок', 'Текст', $cat);
Это, разумеется, приведет к ошибке. Третий аргумент должен быть с типом User, а передан Cat. Ахтунг!
Тайп-хинтинг позволяет избежать ошибок.
А теперь давайте вспомним оператор instanceof. Помните, что при проверке объектов дочерних классов на родительский, он возвращал true? То же актуально и для тайп-хинтинга. Давайте создадим класс Admin, который будет наследником класса User. И попробуем передать его в качестве автора статьи:
class Admin extends User
{
}
$author = new Admin('Пётр');
$article = new Article('Заголовок', 'Текст', $author);
Такой код корректно отработает, потому что Admin – это тоже User. Обратное, разумеется, неверно. Если мы в конструкторе статьи разрешим в качестве автора передавать только объекты класса Admin, то если туда передадим объект класса User, то скрипт упадёт с ошибкой, потому что не всякий User является Admin-ом. Что, согласитесь, вполне логично.
Заключение
Ну вот мы и изучили основы ООП, дальше мы будем учиться их правильно применять, а это уже намного интереснее. До встречи в следующих уроках!
Комментарии