ООП в PHP. Абстрактные классы и интерфейсы.

В процессе поиска новой работы столкнулся с тем, что все работодатели хотят, чтобы было отличное знание ООП в ПХП. Чем я хуже других, подумал я, и решил перечитать главу про объектно-ориентированное программирование. И вот на очередном собеседовании мне задают вопрос — чем абстрактный класс отличается от интерфейса.
Единственное отличие, которое я знал было в том, что один класс может реализовать несколько различных интерфейсов. Но обо всем по порядку.

Абстрактные классы
Абстрактные классы используются в PHP для определения абстрактных объектов. Чтобы понять, что определяет абстрактный объект, давайте рассмотрим такое понятие, как “пища”. Все мы знаем, что такое “пища”, но не всегда смотрим на то, из чего конкретно она приготовлена. Вы видели самые разнообразные виды пищи (бифштексы, цыпленок и тому подобное), однако само по себе понятие пищи является абстрактным — оно существует только как обобщение более конкретных вещей. Подобная идея справедлива также и для абстрактных классов.

В ООП абстрактные классы предназначены для того, чтобы создать суперкласс, который будет определять абстрактные характеристики его классов-наследников. На самом деле абстрактные классы могут содержать в себе какой-либо код, а могут быть вообще без кода; кроме этого, на их основе нельзя создать экземпляр напрямую. Рассмотрим пример:

<?php
   abstract class Number {
      private $value;
      abstract public function value();
      public function reset() {
         $this->value = NULL;
      }
   }
   class Integer extends Number {
      private $value;
      public function value() {
         return (int)$this->value;
      }
   }
   $num = new Integer; /* Все в порядке */
   $num2 = new Number; /* Возникнет ошибка */
?>

Тут мы создали абстрактный класс Number, который является расширением класса Integer. Поскольку класс Number объявлен как abstract, на его основе нельзя создавать экземпляры. Если посмотреть на класс Number, то можно увидеть, что в нем определены две функции: value() и reset(). Абстрактный класс может не содержать код для методов, хотя при необходимости его можно добавить. Что же касается класса Number, то поскольку функция value() является специфической для конкретного типа числа, она реализуется в классе-наследнике. Чтобы разработчик мог реализовать такое поведение в своем коде, используется ключевое слово abstract, указывающее на то, что это просто заполнитель в классе Number. Однако это не относится к методу reset(), который остается неизменным для любого конкретного типа числа.

Интерфейсы
В отличие от абстрактных классов, посредством которых можно выражать абстрактные понятия в программах, интерфейсы предназначены для того, чтобы обеспечить определенную функциональность внутри класса. Если выражаться точнее, то интерфейс представляет собой средство для определения набора методов, которые должен иметь класс, реализующий данный интерфейс. Чтобы использовать интерфейс, его необходимо объявить с указанием ключевого слова interface:

<?php
   interface printable {
      public function printme();
   }
?>

Чтобы интерфейс приносил определенную пользу, он должен быть реализован с помощью одного или нескольких классов. Далее будет определен интерфейс printable, который заявляет, что любой класс, реализующий этот интерфейс, должен реализовать метод printme(). Чтобы создать класс, реализующий подобный интерфейс, в определении класса используется ключевое слово implements, за которым следует список реализованных интерфейсов:

<?php
   class Integer implements printable {
      private $value;
      public function getValue() {
         return (int)$this->value;
      }
      public function printme() {
         echo (int)$this->value;
      }
   }
?>

Здесь определен исходный класс Integer, чтобы реализовать интерфейс printable. Как класс, реализующий этот интерфейс, он гарантирует, что класс Integer предложит все методы, которые определены в интерфейсе.

Теперь, когда определено, что класс реализует заданный интерфейс, можно гарантировать, что любые функции или методы, для которых требуются некоторые действия со стороны класса, получат их, и для этого нет необходимости проверять экземпляр с помощью операции instanceof.

P.S. — Большая часть статьи — не моя, а взята из книги, где, на мой взгляд, наиболее понятно описана разница между абстрактными классами и интерфейсами.
P.P.S. — Так же, не особо понятно зачем вообще нужно ООП в PHP, т.к. прям явных преимуществ я не вижу, а все это можно реализовать с помощью простых функций… Единственный раз, когда я за свою жизнь использовал ООП — это для реализации класса, работающего с базами данных.

10 комментариев

  1. Вооот оно! Уже замучился, эти две вещи — абстрактные классы и интерфейсы давно не дают покоя, читал и там и сям, но вот понять, ЗАЧЕМ это нужно, КОГДА И ГДЕ а главное КАК это использовать — не мог хоть убейся, ваша заметка кажется расставила почти все точки.

    Если я правильно понял, то интерфейс это всего-то навсего своеобразная проверка, для уверенности в том, что все необходимые методы в классе будут?

    С абстрактными классами тоже примерно понятно, но возникает вопрос — а что изменится если я создам точно такой же класс как в примере (ну пару скобок придется добавить), но не укажу что он абстрактный, а потом так же буду перекрывать его методы в потомках? Что изменится в работе? Или слово abstract это больше для программистов, а не для php интерпретатора?

  2. Да, слово abstract это больше для программистов как для людей, любящих порядок. Все ООП для людей (машине ООП не так симпатичен как нам с вами). Убрать колючевое слово abstract и определить функцию как пустую, а потом ее просто переопределять в подклассах можно, но это плохой подчерк.

  3. Не хватает только информации о том, как же проверить, реализует ли подключаемый динамически класс нужный интерфейс. Это удобно, например, для поддержки плагинов и т.п.

  4. Практически все описано хорошо, замечание только по интерфейсам и то пример не показывает реальной работы интерфейса, только синтаксис…

  5. По началу это класс БД, потом это класс для кеширования (уже как минимум 2 или 3 класса), потом это класс для ведения логов, а так-же класс для работы с настройкаами, а так-же наверняка потребуется класс для работы с сессиями ну а потом после это уже понеслось — класс пользователей, класс постов, коментариев и т.д. и т.п… главное в этом деле это начать и правильно оформить структуру проекта.

Добавить комментарий для Сергей Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *