Использование интерфейсов в пользовательской среде OO PHP - PullRequest
14 голосов
/ 04 февраля 2011

Я работаю над довольно простой OO PHP-фреймворком (не очень важным в данном случае, я думаю ...) со следующей базовой структурой:

application/
  classes/
  controllers/
  includes/
  models/
  views/
classes/
includes/

Я знаю, что использую интерфейсы, а не жесткое кодированиеКлассы - это хорошая практика для ООП, но я не уверен, что лучше всего подходит для реального расположения / структуры каталогов и файлов интерфейса.

Должны ли интерфейсы быть разделены на несколько файлов в каталоге:

interfaces/
  iDatabase.php
  iRouter.php

или все они должны быть помещены в один файл, поскольку они не так велики:

includes/
  interfaces.php (with all Interfaces inside)

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

Что вы думаете?Полностью ли я смотрю на это неправильно (я склонен делать это с большинством своих проблем, пока кто-то не направит меня в правильном направлении! Ха-ха)

Спасибо, куча!

Райан

Редактировать 2011-02-07:

Прочитав ответы, которые мне были даны, я попробовал несколько вещей.

Если предположить, что перечисленные ниже классы автоматически загружаются из точного места на диске (база данных будет загружена в 'classes / Database / Database.php'), будет ли эта настройка эффективной?

class Database_Mysql_Database extends Database_DatabaseAbstract implements Database_Database {}

Database_Mysql_Database - это обычный класс, Database_DatabaseAbstract - это абстрактный класс с базовыми методами, общими для различных типов баз данных, Database_Database будет интерфейсом, который будут использовать пользователинапечатайте подсказку, чтобы обеспечить совместимость с их классами.

Я на правильном пути?

Ответы [ 4 ]

11 голосов
/ 04 февраля 2011

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

Допустим, мы имеем дело со слоем абстракции базы данных.У вас будет интерфейс iDatabase и интерфейс iDatabaseDriver.Скажем, структура вашей папки (и класса) выглядит следующим образом:

/classes/database/idatabase.php
/classes/database/database.php
/classes/database/drivers/mysql/databasedrivermysql.php
/classes/database/drivers/postgres/databasedriverpostgres.php

Теперь есть 2 логических места для размещения iDatabaseDriver.Вы можете поместить его в базу данных или под драйверы.Лично я бы поместил его в базу данных, поскольку он держится рядом с тем местом, где он необходим (поскольку более вероятно, что Database требует iDatabaseDriver, поэтому зависимость есть).

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

Теперь этот пример является чрезмерным упрощением, но я думаю, что он должен понять суть.

  • Есть правила для именования и хранения интерфейсов

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

  • Соблюдать эти правила!

    Это важнее, чем наличие правил.Если вы не следуете правилам, это хуже, чем вообще их не иметь, поскольку вы ожидаете чего-то, что не произойдет.

  • Любите семантические отношения по отношению к уровням кода

    Семантические отношения между интерфейсом и его конкретными реализациями важнее, чем отношения, которые интерфейс представляет собой интерфейс.Поэтому поместите семантически связанный код в те же (или похожие) места.

Редактировать: Относительно именования и вашего редактирования:

Лично я ненавижутакие вещи, как Database_Database.Хотя это может иметь смысл с учетом структуры приложения, оно не имеет никакого смыслового смысла.Вместо этого мне нравится проверять наличие файла в автозагрузчике, и, если он не существует, но каталог существует, проверять наличие того же файла внутри этого каталога.Таким образом, Database приведет к проверке /database.php, а в случае неудачи /database/database.php.Это устраняет необходимость двойного именования.Database_DatabaseAbstract станет Database_Abstract.Таким образом, ваш Database_Mysql_Database может стать Database_Mysql сохраненным в /database/mysql/mysql.php (что мне кажется более чистым).

Насколько ваше соглашение об именах абстрактных классов и тому подобное, я личнопредпочитаю идентифицировать интерфейсы по имени.Это облегчает понимание с первого взгляда (Вы знаете, что public function foo(iDatabase $database) ищет экземпляр интерфейса вместо абстрактного класса или конкретного класса).Теперь есть два реальных способа сделать это.

  1. Добавьте Interface к имени, поэтому Database_Database станет Database_Interface.Лично я считаю, что это слишком многословно для моих нужд, однако выгода в том, что все ваши специальные типы классов (исключения, интерфейсы, итераторы и т. Д.) Могут просто отображаться следующим образом.Имя класса говорит вам точно, что у вас есть, без какой-либо двусмысленности.

  2. Добавьте всю последовательность с помощью i.Таким образом, Database_Database станет iDatabase, который затем будет переведен в автозагрузчик в /database/interface.php.Тогда, если бы у вас были более глубокие интерфейсы, iDatabase_Mysql_Query также мог бы работать (что соответствовало бы /database/mysql/query/interface.php.

Что касается абстрактного класса, я бы этого не сделал.Тот факт, что класс является абстрактным, на самом деле не должен иметь ничего общего с его семантическим значением.Абстрактная природа является конструкцией кодирования, а не семантической (абстрактный класс используется только для наследования, поскольку вы используете интерфейс для проверки типов)Поэтому я бы не рекомендовал включать Abstract в название класса.Просто назовите это Database и все будет готово.Он лучше читается семантически (ИМХО) и передает то же значение.

Я надеюсь, что это помогает и имеет смысл ...

6 голосов
/ 04 февраля 2011

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

  • со стандартными соглашениями об именах вы бы назвали интерфейсы Database и Router, а не iDatabase и iRouter.

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

РЕДАКТИРОВАТЬ: (после того, как вопрос отредактирован).

Вы хотите:

  • База данных (classes / Database.php), интерфейс для подсказки типа / завершения кода (если вы настаиваете на наличии интерфейса в первую очередь, но мы не обсуждаем это);
  • Database_Abstract(classes / Database / Abstract.php), класс, который реализует базу данных и имеет общую абстрактную функциональность;пользователи никогда не узнают, что он существует;
  • Database_Mysql (classes / Database / Mysql.php), класс, который расширяет Database_Abstract.Он не должен реализовывать базу данных (Database_Abstract уже делает это);

Вы также можете добавить все пути (и имена) к имени вашей платформы;таким образом, если вы используете некоторые классы из ZF, скажем, для генерации PDF, вы можете сохранить один и тот же автозагрузчик и один и тот же каталог классов и не смешивать их.

Если вы действительно увлекаетесь ООП,Вы можете добавить:

  • Database_Factory (classes / Database / Factory.php) - класс, который имеет единственный метод с именем createDatabase;у метода есть комментарий к документу / ** @returns Database * /, и он принимает конфигурацию базы данных (в виде строки или, может быть, массива, или, возможно, объекта Database_Config, если вы хардкорное ядро);это было бы необходимо, чтобы действительно отделить реализацию от интерфейса.

Что касается ответов, которые дают вам несколько разных идей для именования файлов, рассмотрите их, но примите во внимание, что: - однородность - это большаяценность сама по себе;- если вы придерживаетесь соглашения Pear, вы можете использовать одни и те же автозагрузчики для загрузки классов Pear и многих библиотек PHP;- если придерживаться соглашения груш, вы привыкнете к современному PHP.

5 голосов
/ 04 февраля 2011

В общем, стремитесь:

  • Использовать автозагрузчик.
  • Один файл содержит один класс.
  • Держите интерфейсы рядом с их конкретными реализациями.

Вот хорошая статья на эту тему:

http://weierophinney.net/matthew/archives/254-Why-PHP-Namespaces-Matter.html

1 голос
/ 04 февраля 2011

Если вы используете соглашения об именах PEAR или Zend, интерфейс будет находиться в папке компонентов.

Т.е. класс Myprefix_Router_Interface переводится в путь /Myprefix/Router/Interface.php

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

...