Использование интерфейсов в пользовательской среде OO PHP

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

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

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

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

interfaces/
  iDatabase.php
  iRouter.php

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

includes/
  interfaces.php (with all Interfaces inside)

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

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

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

Райан

Изменить 2011-02-07:

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

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

class Database_Mysql_Database extends Database_DatabaseAbstract implements Database_Database {}

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

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

Ответ 1

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

Скажем, мы имеем дело с уровнем абстракции базы данных. У вас будет интерфейс 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) ищет экземпляр интерфейса вместо абстрактного класса или конкретного класса). Теперь есть два реальных способа сделать это.

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

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

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

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

Ответ 2

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

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

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

EDIT: (после того, как вопрос был отредактирован).

Вы хотите:

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

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

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

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

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

Ответ 3

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

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

Вот хорошая статья по теме:

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

Ответ 4

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

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

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