Pull to refresh

Comments 25

Так все-таки, как в вашем «решении» создаются новые экземпляры нужных пользователю классов?
Способов создания несколько. Все можно посмотреть в доке к Inversion.
Можно создавать экземпляры как в Symfony при помощи контейнера:

$object = $container->get('Class');

Либо при момощи фабрики (при использовании trait):

$user = User::create();
Это просто одна из самых важных вещей в любом IoC-контейнере, а в посте ее и нет.
Да, этот момент упустил. Добавил в пост.
Существенным минусом IoC-контейнера, на мой взгляд, это отсутствие авто-дополнений в IDE. Может вы как то решили эту проблему?
Это, простите, каким образом IoC-контейнеры влияют на авто-дополнения?
Да, из-за этого мне не нравится DI в Symfony.

Если все организовать через интерфесы, то проблем с автодополнением не будет. (Во всяком случае в PhpStorm)
PhpDoc аннотации разве не решают эту проблему?
Проблема с автодополнением решается очень просто c помощью специальной аннотации

/* @var $adapter \Zend\Db\Adapter\Dapter */
$adapter = $this->getServiceLocator()->get('db-adapter');
По моему мнению лучше выделять это в отдельные геттеры.
Да как угодно, я лишь показал как заставить работать автокомплит в IDE
Да, такая возможность есть, но это нужно каждый раз писать, неудобно…
Немного не понял преимуществ идеи.

> Отлично, а что если какой-нибудь другой программист захочет вместо файлов использовать базу данных? Для этого ему нужно создать класс DatabaseStorage, реализовать интерфейс StorageInterface и заменить все вхождения FileStorage. Но изменение библиотеки сулит проблемы с её обновлениями.

Какие именно проблемы появляются?

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

И если ранее в конструктор передавался интерфейс, я после удачной компиляции более менее буду уверен в корректности программы. Передача строки не гарантирует ничего.

Вот именно поэтому в адекватных реализациях IoC используются не магические строки, а жесткая привязка к типу (с проверкой периода компиляции).

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

Обычно эта проблема решается юнит-тестами на контейнер. Но муторно.
Немного не понял преимуществ идеи.

В чем преимущества самого IoC, я, если можно, рассказывать не буду. Идем дальше.

Для того, чтобы реализовать IoC, нам нужно откуда-то брать все зависимости. Эти зависимости могут (а) иметь свои зависимости и (б) иметь свой жизненный цикл.

IoC имеет две основных реализации: Service Locator (SL) и Dependency Injection (DI). Первую я искренне не люблю (а многие считают ее антипаттерном), вторая более требовательна к контрактам. Проблема «зависимостей зависимостей» возникает только во втором из них, проблема жизненного цикла — в обоих.

Какие именно проблемы появляются?

Во-первых, FileStorage мог создаваться по одному на каждый экземпляр потребителя, а DatabaseStorage — быть общим (ну или наоборот; проблема жизненного цикла). Это значит, что вы не сможете просто заменить один конструктор на другой, вам придется заменять конструктор на вызов синглтона.

Во-вторых, у FileStorage один набор собственных зависимостей (которые учитываются при создании), а у DatabaseStorage — другой (проблема зависимостей зависимостей). Это тоже означает, что простой S&R не поможет. Более того, для каждой из этих зависимостей все эти проблемы повторяются заново, и, как следствие, общее количество проблем растет по экспоненте.

Использование DI-контейнера (или специализированного SL вместо обобщенной фабрики/реестра) сводит эти проблемы на нет. При создании объекта через DI-контейнер (или запросе его у SL) все дерево зависимостей объекта создается автоматически, при этом учитывается ассоциированный с каждым объектом жизненный цикл.

Плюс к этому у вас есть единая (более того, конфигурируемая в рантайме, хоть я этого и не люблю) точка контроля того, какие сервисы используются приложением, что упрощает его модификацию.
IoC приколен, но вот что не нравится — это концентрация сложности в конфигах, которые к тому-же plain-text`ом пишутся без code-completion`а и проверок.

Хотя и есть спец-инструменты, которые помагают это всё организовать, однако вот этот момент напрягает.

М.б. этот момент надо уже решать на след. уровне и развивать языки программирования. И возможно какие-то языки уже позволяют это делать. Ruby/python так умеют?

С другой стороны это пожелание а-ля курица-яйцо :)
IoC приколен, но вот что не нравится — это концентрация сложности в конфигах, которые к тому-же plain-text`ом пишутся без code-completion`а и проверок.

А не надо приравнивать конфиги к IoC. Можно делать IoC без (а) конфигов (б) контейнеров и (ц) с полной проверкой в этапе компиляции.

И язык тут не причем совершенно, IoC умеет столько же, сколько умеет язык, в котором он применяется.
Пример в студию или линку — повышу квалификацию Ж)
Пример чего? IoC без конфига и контейнера?

$user = new User(new FileStorage());


Прямо из статьи.

А вообще — вон целая книга про это есть, только не для PHP. Но подходы идентичны.
Это ни хрена не он — мы тут завязываемся в коде на конкретный класс FileStorage.
Адепты IoC закидают.
Эээ… вы, видимо, не вполне понимаете, как работает IoC.

Внутренность User должна быть независимой от FileStorage. А передача снаружи конкретного экземпляра — IoC (через DI, если быть точным) не нарушает. Откуда этот экземпляр взялся — вопрос отдельный совершенно.
Может как раз наоборот?

| In software engineering, inversion of control (IoC) is an object-oriented
| programming practice where the object coupling is bound at run time by
| an assembler object and is typically not known at compile time using static analysis.

Уже вечер — о том ли мы говорим?? :)

Я точно помню, что что-то одно из них входит в другое.
Не наоборот.

Приведенный пример кода — это и есть внутренности assembler object (хотя правильнее сказать composition root).
Я тогда завтра с утра на свежую голову ещё раз проверю — правильно ли я эти понятия понимаю.
Вы просто добавили ещё одну абстракцию, чтобы заложиться на возможные изменения. Вроде того:
— Что если в будущем мы захотим изменить X? Давайте задавать X через переменную/константу/параметр.
Это хороший универсальный приём. Мастерство разработчика, правда, заключается не в том, чтобы его применить, а в том, чтобы знать когда применять, а когда нет.
Sign up to leave a comment.

Articles