В прошлый день я реализовал простой класс, служащий контейнером IOC (не очень похожий) в одном из моих проектов на игровой площадке, и какой-то профессиональный парень сказал мне, что у него будут проблемы при тестировании кода (слишком вежлив, чтобы рассказывать, что именно проблема !!)
хотя я сам всегда использую фреймворки IOC, я хотел знать, почему именно эта методология плохая?
Я даже написал много тестов (используя фреймворки), используя эту методологию, и у меня никогда не было проблем.
Я хочу вашу идею о проблемах дизайна этого:
public class IocContainer{
private static IocContainer instance;
private IocContainer(){
}
public static IocContainer getInstance()
{
if (instance == null){
instance = new IocContainer();
}
return instance;
}
public ChatPresenter getChatPresenter(ChatView chatView)
{
return new ChatPresenterImpl(chatView, getChatRepo() , getMessagingService());
}
private ChatRepo getChatRepo()
{
return new ChatRepoImpl(getDbHelper(), getRemoteService());
}
private MessagingService getMessagingService()
{
return new MessagingServiceImpl()
}
private DbHelper getDbHelper()
{
return new DbHelperImpl();
}
private RemoteService getRemoteService()
{
return new RemoteServiceImpl();
}
}
как вы видите, я сделал доступным только getChatPresenter, и я использую его, как показано ниже, на мой взгляд, и он работает хорошо.
ChatPresenter presenter = IocContainer.getInstance().getChatPresenter(this)
код обрабатывает инверсию управления без какой-либо тесной связи (с использованием интерфейсов).
Я хочу знать что-нибудь неправильное в этом подходе? (Мне нужны ответы с технической точки зрения, потому что я уже знаю, что использование библиотеки контейнеров Ioc проще, имеет больше функций, таких как области и так далее ...)
На самом деле я хочу знать какие-либо проблемы и ограничения, которые этот подход может создать в будущем?
будь жестоким и убей меня: D
Благодарю . это была скорее опечатка, потому что я написал код без IDE.
С концептуальной точки зрения я бы сказал, что на самом деле это не IoC, поскольку ваш класс ChatView все еще контролирует получение докладчика чата - ваш код больше похож на фабричный шаблон.
это класс IocContainer, который сопоставляет интерфейсы с конкретами. как ты это скажешь? пожалуйста, объясните подробнее?
поскольку я знаю, что основная ответственность контейнера Ioc - предоставить конкретный интерфейс. я ошибся ?
Посмотрите здесь: martinfowler.com/articles/injection.html#InversionOfControl. Ваш код в основном выглядит как подход локатора сервисов, но ваш контейнер - это не реестр, который заполняет кто-то другой, а фабрика, которая сама создает экземпляры.
это была чертовски классная статья. Спасибо за это. эта статья никогда не была против Service Locator и даже предполагает, что он более простой. но, как вы сказали, мой не является даже локатором службы, потому что в локаторе службы вы не создаете конкретный экземпляр явно, но вы создаете реестр (без недостатка цепочки зависимостей размытия и всех зависимостей конструктора, использующего мой код). так что технически это будет проблемой? в чем будет его ограничение?
Что ж, ограничения заключаются в том, что IocContainer всегда зависит от конкретной реализации (й) интерфейсов и, следовательно, связан с ними. С реестром услуг вы бы инвертировали это, например чтобы реализации ваших сервисов регистрировались сами или это делал какой-то механизм загрузки. Это позволит вам добавлять или заменять службы без перекомпиляции IocContainer. - По сути, IoC предназначен для уменьшения взаимосвязи, поэтому вы бы потрудились добавить эту сложность в свое приложение.
Вы имеете в виду, что я могу добавить хэш-карту <Interface, {Concrete, params}> и загрузить его где-нибудь на этапе настройки? хорошо, а что, если одна из зависимостей недоступна во время настройки? как докладчик, который хочет, чтобы представление было зависимостью, и оно недоступно, пока пользователь не перейдет к этому представлению?
Это может быть несколько карт или даже наборов, таких как Set<ChatPresenter> presenters и т. д. (И вы выбираете одну, исходя из того, что вам нужно, может быть только первым). Также вам необходимо убедиться, что зависимости разрешаются тогда, когда они необходимы (или раньше), то есть когда представление запрашивает презентатора, он уже должен присутствовать - презентатор должен быть разрешен не позднее, чем в это время. Как правило, во время настройки вы либо предполагаете наличие зависимостей будут, либо разрешаете их отсутствие, либо проверяете, доступны ли они, и прерываете их, если нет (обычно вы делаете это при запуске).
Думаю, у меня есть идея. у меня проблема с тем, что некоторые из моих решений зависят от состояния программы. в некоторых контейнерах IOC существует концепция под названием Module. это модулирует контейнер зависимостей. затем всякий раз, когда открывается какое-либо представление, вы конфигурируете и запускаете этот конкретный модуль представления, отправляя представление в качестве параметра в метод конфигурации. таким образом, вся цепочка зависимостей этого модуля будет присутствовать во время конфигурации (некоторые из них находятся вне вашего контроля)
в моем случае (Android) представления создаются фреймворком, поэтому я не могу создать экземпляр представления. когда представление создается Android (по запросу пользователя), оно попадает в область действия моего кода для представления. тогда мне нужно разрешить ведущего, и представление необходимо во время конфигурации IOC. Итак, теперь я понимаю, почему в библиотеке Dagger Ioc есть модули. это была отличная дискуссия, большое спасибо за вашу помощь.
на самом деле лучшая часть ваших комментариев была часть, о которой вы говорили о том, что мой класс зависит от конкретных объектов, и я не могу добавлять или заменять службы без необходимости перекомпилировать свой код. просто добавьте его в качестве ответа, и я приму его. Я вижу, как различаются проблемы серверной части и клиента. Я, как разработчик Android, никогда не думаю об изменении чего-либо без перекомпиляции, потому что нам не нужно об этом беспокоиться. и отсутствие этого может помешать нам ясно подумать о правильном дизайне кода.
Сделаю это :) Кроме того, существуют разные взгляды на то, что такое хороший дизайн кода. Мир Java имеет тенденцию предоставлять случаи, которых никогда не бывает (например, предоставление механизмов подключаемых модулей, которые не используются), поэтому, если вы полагаете, что вам это никогда не понадобится, тогда более простой дизайн может быть приемлемым.
да, верно. Я думаю, что самая важная вещь, о которой даже Фаулер заявил в своей статье, - это то, что отделение конфигурации от реализации более важно, чем использование локатора служб или контейнера IOC. на самом деле у меня нет: D, но если я разделю конфигурацию, он может немного походить на контейнер IOC.




Подводя итог комментариям:
Ваш подход выглядит как локатор сервисов, описанный здесь Мартином Фаулером: http://martinfowler.com/articles/injection.html#InversionOfControl
Есть большая разница между тем, что описывает Мартин, и вашим кодом: ваш код напрямую создает экземпляры ваших сервисов и, таким образом, является скорее фабрикой, чем реестром.
Основная идея / цель инверсии управления - уменьшить взаимосвязь за счет уменьшения зависимостей, чего не происходит, если ваш контейнер зависит от реализаций службы (которые ему необходимо напрямую вызывать конструкторы). Таким образом, реализация в вашем вопросе несколько противоречит этой основной идее.
Одним из недостатков зависимости от реализаций может быть то, что службы нельзя заменить без перекомпиляции контейнера. Это может не быть проблемой, если у вас, скорее всего, никогда не будет разных реализаций, но если вам это может понадобиться, более полезной была бы служба реестр.
В реестре обычно предусмотрены методы регистрации реализаций услуг и их доставки по запросу. Что доставляется, зависит от ваших потребностей, но это может быть так же просто, как первая найденная реализация, или быть более сложным, например путем сопоставления некоторых параметров (чтобы получить представление об этом, посмотрите параметры и альтернативы точки внедрения CDI).
Реестр обычно сопровождается некоторыми средствами для настройки реестра, например через файлы конфигурации или сканирование пути к классам (автоматический поиск имеющихся плагинов). Однако может быть также достаточно просто написать код, настраивающий реестр. Все зависит от того, от какой связи вам нужно избавиться (чтобы решить проблему, с которой вы столкнулись) и какая связь является приемлемой.
это также отличная статья о типичных ошибках при использовании контейнеров IOC: devtrends.co.uk/blog/…
@AmirZiarati спасибо за ссылку. Когда дело доходит до внедрения зависимостей, я бы порекомендовал вам взглянуть на такие фреймворки, как CDI (часть JavaEE), Guice и т. д. - Я успешно использовал CDI, когда дело доходит до разделения (например, реализации регистрируются в реестре, когда они загружаются JVM).
private static instance;- это даже не компилируется ...