Лучшие практики для контейнера МОК - PullRequest
26 голосов
/ 26 января 2009

Я использую контейнер Unity IOC, и мне просто интересно, как лучше всего получить доступ к контейнеру для нескольких классов.

Должен ли каждый класс иметь член IUnityContainer, а затем передавать контейнер конструктором? Должен ли быть одноэлементный класс с контейнером IOC?

Как насчет разработки asp.net?

Может ли кто-нибудь направить меня в правильном направлении? Благодарю.

Ответы [ 6 ]

21 голосов
/ 08 октября 2009

ИМХО не рекомендуется вставлять весь контейнер в класс или иметь статический локатор службы IoC для всего приложения.

Вы хотите видеть из конструктора класса (давайте назовем его Foo), какие сервисы / объекты он использует для выполнения работы. Это улучшает ясность, тестируемость и разложимость.

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

Если Foo необходимо создать несколько экземпляров службы электронной почты, лучше создать и внедрить EmailServiceFactory (через контейнер IoC), который будет создавать необходимые экземпляры на лету.

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

Теперь, если позже я захочу предоставить разные экземпляры службы электронной почты, я поменяю ее внутри EmailServiceFactory. Я мог бы также поменять всю фабрику, если все службы, которые она создает, нужно поменять местами (например, во время тестирования).

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

Этот подход также имеет преимущество в том, что теперь, когда модуль инициализируется (применяется только к Prism / Modularity), ему не нужно регистрировать все типы объектов, которые он предоставляет в контейнер IoC. Вместо этого он может просто зарегистрировать свой ServiceFactory, который затем предоставляет эти объекты.

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

Наконец, у нас есть еще один прекрасный пример того, как дополнительный слой косвенности решает проблему.

13 голосов
/ 02 сентября 2009

Поместите контейнер IOC на самый высокий уровень / точку входа в процесс и используйте его для внедрения зависимостей во все, что находится под ним.

6 голосов
/ 26 января 2009

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

IUnityContainer container = new UnityContainer();
container.RegisterInstance<IUnityContainer>(container);

классы, которые должны получить к нему доступ, будут иметь следующее свойство:

private IUnityContainer unityContainer;
[Dependency]
public IUnityContainer UnityContainer
{
    get { return unityContainer; }
    set { unityContainer = value; }
}

таким образом, контейнер внедряется всякий раз, когда экземпляр такого класса разрешается / создается.

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

2 голосов
/ 26 января 2009

Если все ваши объекты нуждаются в ссылке на контейнер, вам следует немного переработать код. Хотя все еще предпочтительнее вызывать новое везде, оно по-прежнему рассеивает ответственность за построение графов объектов по всему коду. При таком использовании мне кажется, что я использую больше как ServiceLocator, а не как контейнер IoC.

0 голосов
/ 23 мая 2009

У меня есть пост на эту тему в моем блоге, где я использую что-то вроде ответа t3mujin. Не стесняйтесь использовать его (не беспокойтесь, что это связано с sharepoint ... это не имеет значения):

http://johanleino.spaces.live.com/blog/cns!6BE273C70C45B5D1!213.entry

0 голосов
/ 20 апреля 2009

Другим вариантом будет использование CommonServiceLocator , хотя это может быть бессмысленным косвенным указанием, вы можете использовать ServiceLocator.Current в качестве экземпляра, известного всем классам

...