Использование интерфейсов в пользовательской среде 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
Это отличный способ разделить ваши компоненты и делает организацию логичной при просмотре только имени класса (вы точно знаете, где находятся определенные файлы).