PHP « Зачем в PHP нужны абстрактные классы и интерфейсы?

Зачем в PHP нужны абстрактные классы и интерфейсы?
АК и И - не обязательные атрибуты ООП, многи ЯП их лишены, однако в РНР5 они были добавлены, хрен знает зачем, возможно просто чтобы гордо заявить: "РНР полноценно поддерживает ООП". Возможно я чего-то не понимаю, поясните мне. Спасибо!

1 ответов


Сам столкнулся с тем, что в инете нет простого человеческого объяснения, для чего нужны И и АК. Но они нужны! :)

Интерфейсы позволяют тебе гарантировать, что какой-либо используемый объект (класс) реализует минимально необходимый набор методов (функций). Суть в том, что пока ты пишешь программы сам, ты можешь договориться сам с собой об интерфейсе к чему либо (читай — ты можешь сам для себя решить какой класс какие функции будет реализовывать, как они будут называться, какие параметры у них будут на входе и что они будут возвращать на выходе). Если же разрабочиков несколько, то тебе нужно контролировать код, который еще не написан, и будет написан не тобой. В результате создается интерфейс с перечнем некоторых (или обязательных) функций, которые должен реализовывать класс, их названиями и тем, что у этих функций есть на входе и что должно быть на выходе.

Пример: надо сделать страничку на банковском сайте, где пользователь напишет период с какого-то числа по какое-то число, а в результате получит картиночку с графиком валют за выбранный период. Дальше. У вас, скажем, два PHP-программиста — один джедай в GD или в ImageMagick и может клево реализовать эту картиночку с графиком (с тенюшечками, градиентом, блекджэком и шлюхами :), а другой хорошо знает, откуда и как брать данные по курсам валют, как парсить эти данные и как их сложить в читабельный массив для первого чувака, который из этих данных сделает картиночку с графиком. Причем фишка еще в том, что им обоим совершенно по-барабану, каким образом каждый из них реализует свои функции, так как у них есть некий интерфейс (который, например, составил тимлид или один из этих двух программеров или вместе, неважно), в котором видно, какие функции должны быть реализованы, что они должны возвращать и как называться. Своеобразный «черный ящик» получается для каждого программиста. (Например, GD-джедай на первых порах вообще не при делах, он ждет пока второй чувак реализует свою часть работы — напишет ф-ии по сбору данных по курсам валют, напишет ф-ии по распарсиванию этих данных и по собиранию этих данных в массив — всё в согласии с интерфейсом (но это не значит, что он не может в интерфейсе что-то изменять или править). Потом GD-джедай видит в интерфейсе какие ф-ии он должен реализовать и что делают и возвращают другие ф-ии, написанные вторым чуваком и использует их в написании своей части кода, и ему совершенно не важно, как именно написаны эти функции от второго чувака — он знает только то, что эти функции возвращают/принимают и всё.)

Пример высосан из пальца — на самом деле в этом случае, конечно, никаких интрефейсов объекта делать не нужно, да и программист всего один нужен, но когда задача в 10 раз масштабнее, с десятками классов и 3-я программистами, то смысл в интерфейсах есть. Посмотрите в код, скажем, Zend Framework'а — там тоже есть интерфейсы (они в отдельном файлике), по ним можно быстро определить что да как делает некий класс, не вдаваясь в подробности каким образом и не читая код самого класса (который тоже в отдельном файлике).

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

Пример: класс для работы с БД. Допустим, есть такие методы:

  • select — составляет запрос select (из каких таблиц делать выборку, по каким полям, и прочие критерии)
  • check — проверяет запрос на безопасность
  • exec — собственно, выполняет запрос и возвращает выборку (результаты запроса) из базы
Реализация select и check одинакова для всех баз данных — SQL везде остается SQL'ом, проверка на htmlspecialchars(), addslashes() и прочее тоже одинаковая для любых строк (запросов). Поэтому мы можем создать абстрактный класс с названием DB, который будет реализовывать методы select() и check() и от которого будут наследоваться все остальные классы для работы с какой-либо базой (в которых уже будет реализован сам exec()):

<?php
abstract class DB {
    abstract protected function exec($sql);

    public function select($array) {
         // тут манипуляции с $array и составление SQL-запроса в переменную $sql

        $this->check($sql); // проверка у нас уже реализована ниже
        $this->exec($sql); // этот метод будет реализован не в абстрактном классе, а в наследующих наш DB-класс классах
    }
 
    protected function check($sql) {
        // тут некая проверка запроса, заэскейпивание кавычек и т.п.
    }
 
    // прочие методы
}
 
Сам по себе класс DB бесполезен. Однако, если сделать потомков mysqlDB, mysqliDB, mssqlDB, oracleDB (ну, типа, «class mysqlDB extends DB»), то в них надо будет только реализовать метод exec() (мы его, образно говоря, «перезапишем» в нашем классе mysqlDB), а методы select() и check() уже будут реализованы (но и их мы тоже, конечно, можем «перезаписать», если вдруг будет такая необходимость, прям внутри класса mysqlDB).

Вот.

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


abstract function exec ($sql)
 
И, например


function exec ($sql)
{
exit('!Вызов абстрактного метода!');
}
 
Это по сути одно и то же.

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

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


За Вами выехала инквизиция.

Затем, зачем и в C++ и Java.
А вот в Ruby нинада)) Но я все равно написал гем для определения абстрактных классов) https://rubygems.org/gems/simple_abstract