Я создаю действительно минимальную структуру MVC, чтобы увеличить свои знания PHP и бросить вызов себе. Я пришел к тому, что классы начинают зависеть друг от друга, чтобы работать. Инъекция зависимостей , по-видимому, является решением этого и используется некоторыми крупными средами .
Я нашел Bucket на Github и немного испортил его, чтобы понять основы. Однако я не могу опустить голову, когда нужно создать контейнер?
Создание одного большого контейнера, включая все возможные классы, которые могут потребоваться, кажется мне не контрпродуктивным, и я не могу представить, что это хорошая практика. Похоже, что рецепт плохой производительности.
В альтернативном варианте, который заключается в создании нескольких контейнеров, я до сих пор не понимаю, как постоянно вонючие синглтоны больше не нужны.
Предположим, что у меня будет следующий код:
$session_container = new bucket_Container(); $session_container->create('Database'); $session_container->create('Database_Sessions'); $log_container = new bucket_Container(); $log_container->create('Database'); $log_container->create('Database_Log');
Итак, здесь у нас есть два контейнера или в этом случае ковши для двух совершенно разных применений, которые взаимно зависят от их зависимости от класса Database
.
Моя логика говорит мне, что приведенный выше код создаст два независимых экземпляра класса Database
class, что означает, что мне все равно придется сделать Database
-class singleton, чтобы убедиться, что параллельные экземпляры моего подключения к базе данных не происходят?
Это верно?
Я не знаю много о конкретной библиотеке, но при условии, что она позволяет использовать фабрику, пусть фабрика вернет тот же экземпляр.
Изменить: Хорошо, это просто на индексной странице Bucket GitHub.
class MyFactory { function new_PDO($container) { return new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret"); } } $bucket = new bucket_Container(new MyFactory()); $db = $bucket->get('pdo');
Поэтому в вашем случае вы можете просто сделать:
class MyFactory { private $pdo; function new_Database($container) { if($this->pdo){ return $this->pdo; } return $this->pdo = new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret"); } } $factory = new MyFactory(); $session_container = new bucket_Container($factory); $session_container->create('Database_Sessions'); $log_container = new bucket_Container($factory); $log_container->create('Database_Log');
Что-то вроде того. Не похоже на ракетную науку.
Edit2: у меня недостаточно комментариев, чтобы прокомментировать вопрос (немного глупо), но в ответ на вашу «модульность»: подумайте о контейнере как о «клеве» вашего приложения. Действительно, если у вас большое приложение, вы можете захотеть «приклеить» только в изолированной части вашего приложения. Это допустимая проблема инкапсуляции. Но даже тогда вам все еще нужен контейнер, который обрабатывает инъекцию на самом высоком уровне абстракции. Если вы просто создаете отдельный контейнер для каждой части вашего приложения, вы либо получаете ненужное дублирование экземпляров, либо вам нужно применить другой уровень управления экземплярами, который никак не улучшает инкапсуляцию: вы все еще обмена экземплярами между различными частями вашего приложения.
Мой совет – использовать один контейнер на уровне начальной загрузки. Если вы хотите добавить инкапсуляцию для определенных частей приложения (модулей, плагинов и т. Д.), Используйте «дочерние контейнеры». Детский контейнер наследует экземпляры из родительского контейнера, но родительский контейнер ничего не знает о ребенке (насколько он заинтересован, он все еще холостяк;)). Может быть, Bucket поддерживает это по умолчанию, я знаю, что другие контейнеры DI. Если нет, это очень легко реализовать с помощью Decorator. Представьте себе что-то вроде этого:
class MyContainerType extends bucket_Container { private $_parent; private $_subject; public function __construct($factory = null, bucket_Container $parent = null){ $this->_parent = $parent; $this->_subject = new bucket_Container($factory); } public function get($key){ $value = $this->_subject->get($key); if($value){ return $value; } return $this->_parent->get($key); } /** * Override and delegation of all other methods */ }
Создание одного большого контейнера, включая все возможные классы, которые могут потребоваться, кажется мне не контрпродуктивным, и я не могу представить, что это хорошая практика. Похоже, что рецепт плохой производительности.
Напротив. Это именно то, что вы сделали бы с контейнером di. Контейнер будет создавать объекты только по требованию, поэтому на самом деле нет никаких накладных расходов для управления всеми вами одноэлементными классами через него.
Самая большая проблема с di заключается в том, чтобы различать общие объекты (вещи, которые вы обычно считаете одиночными) и переходные объекты (объекты, которые имеют множество экземпляров через обычный поток приложений). Первые легко управляются через di. Последние не подходят. Наличие этих двух «видов» объектов, четко различающихся, может показаться немного хлопотным, но действительно очень выгодным побочным эффектом использования контейнера di.
Если вас беспокоит несколько одновременных подключений, вы можете просто использовать mysql_pconnect () или equivelant для используемой базы данных. Он будет проверять, открыто ли соединение и использовать существующее соединение, если оно есть.
Что касается проблемы с контейнером, я видел, как это было сделано двумя способами, о чем вы, похоже, осознаете оба. Первый способ состоит в том, чтобы инфраструктура считывала вашу схему базы данных и создавала классы для каждой таблицы. Мне лично это не нравится. Symfony – это одна структура, которая делает это (используя доктрину ORM).
Более предпочтительный метод, который я видел, это иметь общий контейнер, который в основном создает sql для вас, для таблицы, столбцов и действия. Это подход, применяемый codeIgniter:
$query = $this->db->get('mytable'); $query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);