Ищете элегантный способ загрузки зависимостей / сервисов / конфигурации в PHP? - PullRequest
2 голосов
/ 09 ноября 2011

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

До сегодняшнего дня я использовал синглтоны,реестр и в последнее время контейнер внедрения зависимости.В то время как многие люди утверждают, что DI - это путь, мне кажется, что это справедливо переносит проблему соединения между компонентами в другое место.

Синглтоны вводят глобальное состояние, реестр вводит тесную связь, а DI вводит ..Ну, много сложностей.Я все еще в замешательстве и не могу найти правильный способ связать свои занятия друг с другом.

Тем временем я придумал индивидуальное решение.На самом деле это не решение, оно просто абстрагирует реализацию загрузки служб из моего кода.

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

abstract class Base_Component {
    function _load_service($service) {
        // could be either
        return DI_container::getInstance()->$service;

        // or
        $class = '\services\\'.$service;
        return new $class;

        // or other implementation
    }
}

Реализация их загрузки теперь реализована только в одном месте, базовом классе, так что, по крайней мере, я избавился от строк кода, подобных следующему, в мои компоненты:

$database = new Database(Registry::getInstance()->load('db_config'));

или

$database = DI_container::getInstance()->database;

Теперь, если мне нужен экземпляр базы данных, я делаю это

$database = $this->_load_service('database');

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

Но, как я уже сказал, я даже не совсем уверен, какой метод я буду использовать для загрузки классов и конфигурации.

Ваше мнение?

Ответы [ 2 ]

1 голос
/ 09 ноября 2011

Зачем изобретать велосипед?Используйте Pimple в качестве контейнера DI и узнайте, как его использовать, из его документации.

Или используйте микрофрамму Silex в качестве основы для создания своей собственной инфраструктуры.Он расширяет функциональность Pimple, поэтому вы можете использовать внедрение зависимостей.

Чтобы ответить на ваш вопрос, вы используете DI без привязки к нему ваших классов:

interface ContainerInterface {
    public function getService($service_name);
    public function registerService($service_name,Closure $service_definition);
}

class Application {
    public function __construct(ContainerInterface $container) {
        $this->container= $container;
    }

    public function run() {
        // very simple to use!
        $this->container->getService('db')->someDatabaseQuery();
    }
}

$c = new My_DI_Container;

// Service definitions could be in a separate file
$c->registerService('db',function() { return new Database('some config'); });

// Then you inject your DI container into the objects that need it
$app = new Application($c);
$app->run(); // or whatever

Таким образом,Контейнер DI отделен, и в будущем вы можете использовать другую реализацию.Единственное требование состоит в том, что он реализует интерфейс ContainerInterface.

Обратите внимание, что объект контейнера выдвигается, а не вытягивается.Избегайте использования синглтона.Чтобы получить / установить объекты одного экземпляра, используйте контейнер (это его ответственность).А чтобы получить экземпляр контейнера, просто протолкните его через конструкторы.

0 голосов
/ 09 ноября 2011

Ответ на свой вопрос; Посмотрите на PHP автозагрузку . Регистрация классов с помощью автозагрузки делает это таким образом, что вам не нужно помещать require / include везде, что действительно положительно влияет на RAD (быстрая разработка приложений).

Мои мысли:

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

Меня не волнует внедрение зависимости. ООП основан на инкапсуляции, вводит один объект в другой, imo, нарушает эту инкапсуляцию. Когда вы вводите объект в другой объект, целевой объект должен «верить», что ничего относительно введенного объекта не изменилось, иначе вы можете получить необычное поведение.

Рассмотрите интервал имен ваших классов (не интервал имен PHP, а добавьте префикс к вашей среде, как это делает Zend, Zend_), это поможет вам зарегистрировать пространство имен, тогда, когда класс вызывается, автозагрузчик гарантирует, что правильный класс загружен. Вот как работает Zend_Framework. За подробностями обращайтесь Zend_Loader_Autoloader . Фреймворк Symfony фактически делает этот шаг вперед; во время первого запроса он просматривает все известные местоположения в поисках файлов классов, затем создает массив классов и путей к файлам, а затем сохраняет массив в файл (кеширование файлов), поэтому последующие запросы не будут иметь такие же накладные расходы. Что-то, чтобы рассмотреть для своей структуры.

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

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

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

...