PHP « Зачем в PHP нужны абстрактные классы и интерфейсы?
АК и И - не обязательные атрибуты ООП, многи ЯП их лишены, однако в РНР5 они были добавлены, хрен знает зачем, возможно просто чтобы гордо заявить: "РНР полноценно поддерживает ООП". Возможно я чего-то не понимаю, поясните мне. Спасибо!
1 ответов
Сам столкнулся с тем, что в инете нет простого человеческого объяснения, для чего нужны И и АК. Но они нужны! :)
Интерфейсы позволяют тебе гарантировать, что какой-либо используемый объект (класс) реализует минимально необходимый набор методов (функций). Суть в том, что пока ты пишешь программы сам, ты можешь договориться сам с собой об интерфейсе к чему либо (читай — ты можешь сам для себя решить какой класс какие функции будет реализовывать, как они будут называться, какие параметры у них будут на входе и что они будут возвращать на выходе). Если же разрабочиков несколько, то тебе нужно контролировать код, который еще не написан, и будет написан не тобой. В результате создается интерфейс с перечнем некоторых (или обязательных) функций, которые должен реализовывать класс, их названиями и тем, что у этих функций есть на входе и что должно быть на выходе.
Пример: надо сделать страничку на банковском сайте, где пользователь напишет период с какого-то числа по какое-то число, а в результате получит картиночку с графиком валют за выбранный период. Дальше. У вас, скажем, два PHP-программиста — один джедай в GD или в ImageMagick и может клево реализовать эту картиночку с графиком (с тенюшечками, градиентом, блекджэком и шлюхами :), а другой хорошо знает, откуда и как брать данные по курсам валют, как парсить эти данные и как их сложить в читабельный массив для первого чувака, который из этих данных сделает картиночку с графиком. Причем фишка еще в том, что им обоим совершенно по-барабану, каким образом каждый из них реализует свои функции, так как у них есть некий интерфейс (который, например, составил тимлид или один из этих двух программеров или вместе, неважно), в котором видно, какие функции должны быть реализованы, что они должны возвращать и как называться. Своеобразный «черный ящик» получается для каждого программиста. (Например, GD-джедай на первых порах вообще не при делах, он ждет пока второй чувак реализует свою часть работы — напишет ф-ии по сбору данных по курсам валют, напишет ф-ии по распарсиванию этих данных и по собиранию этих данных в массив — всё в согласии с интерфейсом (но это не значит, что он не может в интерфейсе что-то изменять или править). Потом GD-джедай видит в интерфейсе какие ф-ии он должен реализовать и что делают и возвращают другие ф-ии, написанные вторым чуваком и использует их в написании своей части кода, и ему совершенно не важно, как именно написаны эти функции от второго чувака — он знает только то, что эти функции возвращают/принимают и всё.)
Пример высосан из пальца — на самом деле в этом случае, конечно, никаких интрефейсов объекта делать не нужно, да и программист всего один нужен, но когда задача в 10 раз масштабнее, с десятками классов и 3-я программистами, то смысл в интерфейсах есть. Посмотрите в код, скажем, Zend Framework'а — там тоже есть интерфейсы (они в отдельном файлике), по ним можно быстро определить что да как делает некий класс, не вдаваясь в подробности каким образом и не читая код самого класса (который тоже в отдельном файлике).
В абстрактном классе реализуют методы общие для потомков. Также то, что написано выше для интерфейсов, справедливо и для абстрактных классов. Только если в интерфейсах, грубо говоря, псевдокод, то в абстрактных классах может быть и псевдокод и уже готовые рабочие методы.
Пример: класс для работы с БД. Допустим, есть такие методы:
- select — составляет запрос 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