Объектно-ориентированный подход в PHP

31.05.2018 в 09:12
11321
+746

В этом уроке мы подошли к той теме, ради которой были сделаны все предыдущие уроки этого курса. Мы изучили основные возможности объектно-ориентированного программирования в 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-ом. Что, согласитесь, вполне логично.

Заключение

Ну вот мы и изучили основы ООП, дальше мы будем учиться их правильно применять, а это уже намного интереснее. До встречи в следующих уроках!

loader
31.05.2018 в 09:12
11321
+746
Логические задачи с собеседований