tag:blogger.com,1999:blog-8596733192274108952.post5445302861543647585..comments2024-03-12T06:00:18.305+02:00Comments on Programming stuff: DI Паттерны. Service LocatorSergey Teplyakovhttp://www.blogger.com/profile/14300835272589262297noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-8596733192274108952.post-22189850203932836452017-10-25T18:23:10.026+03:002017-10-25T18:23:10.026+03:00Вроде бы, это понятный trade-off для ленивости. Мо...Вроде бы, это понятный trade-off для ленивости. Можно не все зависимости огульно делать ленивыми, а вынести только тяжёлые, понимая о поздних ошибках. Зато все зависимости (и "сиюминутные", и ленивые) будут видны сразу в конструкторе.realsonichttps://www.blogger.com/profile/10979340706480868238noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-44754296329177165622016-08-17T19:56:42.880+03:002016-08-17T19:56:42.880+03:00Роман, да, в случае с Lazy мы вернем ленивость, но...Роман, да, в случае с Lazy мы вернем ленивость, но тут есть проблема: ленивость по своей природе имеет свои плюсы и минусы. В качестве плюсов: мы не получаем потенциально дорогую в создании зависимость, если эта зависимость не нужна. А в плане минуса: если произойдет ошибка при получении зависимости, то она (ошибка) произойдет в произвольный момент работы объекта, а не в момент конструирования.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-86378079946537548112016-08-17T19:31:13.085+03:002016-08-17T19:31:13.085+03:00Спасибо, за статью! Как всегда, очень полезно. Сер...Спасибо, за статью! Как всегда, очень полезно. Сергей, подскажите пожалуйста, разве нельзя избавиться от проблемы: "...В этом случае мы жертвуем «ленивостью» получения зависимостей..." - воспользовавшись Lazy ?Romanhttps://www.blogger.com/profile/03869044748498061386noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-66818011059351852222014-07-17T08:29:44.989+03:002014-07-17T08:29:44.989+03:00Основа это - Dependency Injection != using a DI co...Основа это - Dependency Injection != using a DI container Александрhttps://www.blogger.com/profile/16511771357809704645noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-64739145163005982182014-07-17T08:29:04.137+03:002014-07-17T08:29:04.137+03:00в этой статье я нашел ответ на свои вопросы - http...в этой статье я нашел ответ на свои вопросы - http://www.loosecouplings.com/2011/01/dependency-injection-using-di-container.htmlАлександрhttps://www.blogger.com/profile/16511771357809704645noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-82025745719979219522014-07-16T22:35:56.277+03:002014-07-16T22:35:56.277+03:00Александр, все верно.
> Честно не могу понять ...Александр, все верно.<br /><br />> Честно не могу понять соли.<br />Самый простой вариант понять соль DI - вспомнить о классических паттернах, построенных на его основе. Стратегия передается в конструктор или метод, команда - аналогично. Декоратор - чистый constructor injection. Во всех этих паттернах не используется сервис локатор, но при этом польза от DI есть: мы получаем слабосвязанный дизайн.<br /><br />Ну а проблемы сервис локатора, вроде как, описаны именно в этой статье:)Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-11224159254191411662014-07-16T16:20:51.663+03:002014-07-16T16:20:51.663+03:00Сергей спасибо за ответы, с вашего позволения посл...Сергей спасибо за ответы, с вашего позволения последний пример:<br /><br /><br />это где то в классах инициализации<br /><br />/**<br /> * Get service config<br /> */<br />public function getServiceConfig()<br />{<br /> return array(<br /> 'factories' => array(<br /> 'Application\Model\ModelManager' => function($serviceManager)<br /> {<br /> // инжектим необходимые зависимости в конструктор<br /> return new Model\ModelManager($serviceManager-><br /> get('Zend\Db\Adapter\Adapter'), $serviceManager->get('Cache\Static'));<br /> },<br /> ...<br /> )<br /> )<br />}<br /><br />// а здесь юзаем контейнер, в данном примере получаем модель<br /><br />class AclAdministrationController extends AbstractAdministrationController<br />{<br /> /**<br /> * Model instance<br /> * @var object <br /> */<br /> protected $model;<br /><br /> /**<br /> * Get model<br /> */<br /> protected function getModel()<br /> {<br /> if (!$this->model) {<br /> $this->model = $this->getServiceLocator() // сам контейнер заинжектен где то в базовом обстрактном классе<br /> ->get('Application\Model\ModelManager');<br /> }<br /><br /> return $this->model;<br /> }<br /> ....<br />}<br /><br />Отсюда получаем, что мы используем DI в инициализации объектов, но сам паттерн называется ServiceLocator????<br />Честно не могу понять соли. Вопрос нафига нам использовать DI без контейнеров, а если юзаем контейнеры они называются Service Locator и еще подходят по некоторым характеристикам на Анти Паттерн.<br /><br />PS. Спасибо за терпение, я бы себя уже послал :)<br /><br /><br />Александрhttps://www.blogger.com/profile/16511771357809704645noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-39103919348928911552014-07-16T16:02:14.351+03:002014-07-16T16:02:14.351+03:00Сервис локатор - это использование контейнера напя...Сервис локатор - это использование контейнера напямую и все. DI - это передача зависимостей через конструктор, свойства или методы. Если в качестве зависимости передается сам контейнер и класс сам выгребает нужные ему зависимости, то такий тип (или паттерн) инверсии управления будет называться сервис локатором. <br />З.ы. С зенд фреймворком не знаком. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-78918123914549195232014-07-16T15:41:56.090+03:002014-07-16T15:41:56.090+03:00Я не знаю знакомы ли вы с Zend Framework 2,
но та...Я не знаю знакомы ли вы с Zend Framework 2, <br />но там как раз используются оба этих паттерна на сколько я знаю. <br />По умолчанию вы можете забивать service locator фабриками или анонимными функциями которые в свою очередь возвращают вам сформированный объект,<br /> вы как клиент не знаете каким образом был сформирован данный объект. <br /> Вы просто работаете с полученным объектом. <br /> Также в данном фреймворке есть возможность самому формировать через Di объекты, управляя зависимостями (что заинжектить в нужный нам класс и как).<br /><br />На сколько я понял отличие этих подходов - Service Locator - Ты знаешь где конкретно взять тот или иной объект. DI же сам производит поиск необходимых частей для сбора объектов<br />т.е ты не в курсе откуда он их берет, (поиск по интерфейсу, хинтам итд).<br /><br />Так вот прав ли я ??? Александрhttps://www.blogger.com/profile/16511771357809704645noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-30148788831894816822014-07-16T15:30:49.500+03:002014-07-16T15:30:49.500+03:00DI - это общая концепция, которая заключается в пе...DI - это общая концепция, которая заключается в передаче требуемых зависимостей определенному типу. Сервис локатор - это использование контейнера нпосредственно бизнес-объектами. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-47113602949077247012014-07-16T15:08:09.222+03:002014-07-16T15:08:09.222+03:00Не могли бы вы дать конкретные отличия DI от servi...Не могли бы вы дать конкретные отличия DI от service locator? Тут много пишут о какой то тонкости, но я ее к сожалению так и не увидел в ваших постах.Александрhttps://www.blogger.com/profile/16511771357809704645noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-28091183302601905792013-04-03T22:20:14.687+03:002013-04-03T22:20:14.687+03:00Очень хорошая статья :) Только недавно задумывался...Очень хорошая статья :) Только недавно задумывался над этим вопросом, и статья подкрепила и расширила мои мысли на счет этого паттерна. Использование сервис локатора вполне оправдано, когда есть либо отложенная инициализация либо пере-инициализация отельных кусков функционала без перезапуска системы. Например через сервис локатор была реализована инициализация драйверов для терминального ПО (человек подключает к COM-порту новое устройство, в терминале подгружается новый драйвер).<br />Если взять специфический случай, например инициализация чего-то тяжелого (и лучше чтобы это была Lazy иницализация), то я бы передавал, типизированный Lazy инициализатор:<br />public interface IConcreteLazy <br />{<br /> IConcreteComponent GetInstance();<br />}<br />На счет инфраструктуры я также согласен с Сергеем, лучше использовать Ambient Context, нежели везде резолвить и "прикапывать" инстанс логгера по типу ILog _logger = Locator.Resolve, получается что логгер является состоянием объекта или типа, что не совсем верно.<br />Так же для меня было бы странным видеть в бизнес типе (пусть этот тип использует какой-нить провайдер) зависимость от какого-то там локатора, опять таки при модульном тестировании мне надо сначала создать мок на локатора, только потом передать ему мок на провайдер, в действительности мок на локатор мне совсем не нужен.<br /><br /><br /><br />Славаhttps://www.blogger.com/profile/01633245188932975582noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-33765900380337904972013-03-27T12:06:30.113+02:002013-03-27T12:06:30.113+02:00+100500 :)+100500 :)eugenehttps://www.blogger.com/profile/00368111825500921630noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-22498537754866441682013-03-26T21:36:55.742+02:002013-03-26T21:36:55.742+02:00Жень, я слегка троллил:) Я согласен с твоими рассу...Жень, я слегка троллил:) Я согласен с твоими рассуждениями, тем более я знаю, что в твоих руках использование локатора будет именно вынужденной мерой. (тут я не тролю!)<br /><br />Второй главный вывод (помимо того, как бороться с уже существующим кодом, завязанным на локатор) такой: стараться не писать новый код, завязанный на локатор, выдавая его за "тестабельное" решение и "слабую свзанность".Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-63153034392040676552013-03-26T21:32:11.994+02:002013-03-26T21:32:11.994+02:00Не :). Никто не спорит, что лучше DI. Просто мне д...Не :). Никто не спорит, что лучше DI. Просто мне довелось видеть код, в котором был как раз локатор. И кстати, очень утверждали, что это DI в полный рост :). И в чем его преимущество, что он был довольно неплохо покрыт тестами. Так вот с моей точки зрения, код с локатором, лучше чем код с жесткими связями (хотя бы из-за тестирования). Никто не ставил под сомнение, что рефакторить надо в сторону DI. Я сказал, что легаси код с локатором, лучше чем легаси код с жесткими связями. Ну и как от этого кода с локатором идти в сторону DI. Как по мне самая ценная часть в этой статье - именно о том, чтобы заменять in place сервисы на получение сервисов в конструкторе. Это уже ОЧЕНЬ серьезный шаг к DI. Ну а дальше - сломать парадигму и НЕ ОСТАВИТЬ временное решение. Для легаси кода, а это наш случай, я бы предпочел именно step by step. Если сразу менять использование in place получение сервисов на Constructor Injection велик риск получить не работающую программу. Перенос сервисов в конструктор и убирание ленивости уже может внести ошибки (например, часть кода вообще никогда не работала и фейловый сервис не срабатывал) а не "ленивый" вызов в конструкторе может все сломать. А если добавить еще и формирование Composition Root. В общем, ИМХО за один шаг такое делать нельзя. Ну или можно, если ты ковбой :)eugenehttps://www.blogger.com/profile/00368111825500921630noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-1561706287997108142013-03-26T11:20:56.789+02:002013-03-26T11:20:56.789+02:00@Astellar: Visio 2013.@Astellar: Visio 2013.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-78871981207166280232013-03-26T11:20:31.548+02:002013-03-26T11:20:31.548+02:00@eugene: как по мне, Сервис Локатор возможен как в...@eugene: как по мне, Сервис Локатор возможен как временное решение на 0-й итерации, но поскольку временные решения очень часто становятся постоянными, то я бы не рисковал с таким подходом.<br />Кроме того, если уже взялись за легаси код и решили инвестировать в его улучшение, то нет смысла менять жесткое решение на решение с сервис-локатором. Мы можем поменять шило на мыло.<br /><br />Я бы, как обычно, начал с "низов", убирая из классов лишние зависимости, а также попытался понять, где завязана логика, чтобы очистить этот код от лишних связей.<br /><br />В целом, я понимаю, что подход с локатором может быть не плохим, но, опять таки, меня беспокоит то, что это может превратиться в постоянное решение.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-22227106606964145572013-03-26T07:36:29.826+02:002013-03-26T07:36:29.826+02:00Самый глупый вопрос из всех возможных... а чем вы ...Самый глупый вопрос из всех возможных... а чем вы рисуете диаграммы?Anonymoushttps://www.blogger.com/profile/03999939535124861861noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-69864063298060776232013-03-26T07:28:16.998+02:002013-03-26T07:28:16.998+02:00Знаешь, согласен. Локатор - антипаттерн. Но посмот...Знаешь, согласен. Локатор - антипаттерн. Но посмотри с другой стороны. Имеем код с жесткими зависимостями ( в конструкторе через new). Или же код с Сервис Локатором. Чтобы ты предпочел? Вариант с рефакторингом через DI не предлагать (больше 10 мегабайт исходников). Как минимум такой код можно тестировать. С чем согласен - второй путь использования (без ленивости) МНОГО лучше. Поэтому лучше рефакторить от первого варианта использования ко второму, а потом, если будет время - перейти на DI нормальный.eugenehttps://www.blogger.com/profile/00368111825500921630noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-87848362358787709992013-03-25T18:44:09.628+02:002013-03-25T18:44:09.628+02:00В таком случае я бы посоветовал как минимум следую...В таком случае я бы посоветовал как минимум следующее: протаскивайте инфраструктуру как угодно, но протаскивайте бизнес-зависимости явно.<br /><br />Я бы предпочел вообще для инфраструктурных зависимостей <a href="http://sergeyteplyakov.blogspot.com/2013/01/blog-post.html#AmbientContext" rel="nofollow">Ambient Context</a>, а для бизнес-зависимостей - конструктор.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-29672743884257811012013-03-25T18:40:29.045+02:002013-03-25T18:40:29.045+02:00Сейчас работаем с PHP проектом на Symfony, и там э...Сейчас работаем с PHP проектом на Symfony, и там эта проблема как раз актуальна. Всё построено вторым способом: передаём в конструктор serviceContainer $container и расщепляем в защищённые поля.<br /><br />Перечислять по 6-8 параметров в аргументах конструктора и конфигурационном файле или обрамлять сеттерами не очень хочется. А иначе протащить до самого низа логгер, мэйлер и SMS-мессенджер было бы не так просто.Anonymoushttps://www.blogger.com/profile/12272878224692588218noreply@blogger.com