Именованные сервисы в контейнерах IoC - плохая идея? - PullRequest
5 голосов
/ 12 июля 2011

Кажется плохой идеей использовать сервисные ключи (или «именованные сервисы») при создании контейнера.

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

Например, в настоящее время у меня есть следующий интерфейс, который реализуется следующими классами:

  • IListSerializer
    • CheckboxListSerializer
    • TreeViewListSerializer

У меня также есть бесчисленное множество классов, которые зависят от одного или обоих этих классов. Однако, AFAIK, я должен ссылаться на IListSerializer как на мою зависимость, а не на реализацию. Это означает, что я должен использовать ключи / имена, чтобы различать их, и именно здесь он начинает выглядеть ужасно.

Я вижу свои варианты как один из следующих:

  • Аннотируйте параметры конструктора (зависимости) с помощью ключей. Пары с контейнером IoC.
  • Выполнить ручное подключение в корне композиции. Добавляет повторяющиеся раздувания.
  • Ссылка на классы вместо интерфейса. Похоже, взломать только для удовлетворения контейнера IoC.

Есть предложения?

1 Ответ

9 голосов
/ 13 июля 2011

В целом, принцип замещения Лискова является очень полезным руководством при разработке компонентов и сервисов для IoC. Если две реализации службы не могут использоваться взаимозаменяемо во время выполнения, тогда служба слишком общая, чтобы иметь смысл. В этом сценарии я бы хотел использовать что-то вроде IListSerializer<T>, если это вариант для вас.

Однако, если вы хотите использовать именованные службы, это легко и ненавязчиво настроить с помощью Autofac.

Сначала зарегистрируйте каждый сериализатор по имени:

builder.RegisterType<CheckBoxListSerializer>()
    .Named<IListSerializer>("checkBoxSerializer");
builder.RegisterType<TreeViewListSerializer>()
    .Named<IListSerializer>("treeViewSerializer");

Затем добавьте глобально доступный параметр, который использует имя параметра конструктора, чтобы выбрать правильную реализацию. Мы можем сделать это с помощью модуля:

class NamedParameterResolutionModule<TService> : Module
{
    Parameter _attachedParameter = new ResolvedParameter(
        (pi, c) => pi.ParameterType == typeof(TService),
        (pi, c) => c.ResolveNamed<TService>(pi.Name));

    protected override void AttachToComponentRegistration(
        IComponentRegistry registry,
        IComponentRegistration registration)
    {
        registration.Preparing += (s, e) => {
            e.Parameters = new[] { _attachedParameter }.Contact(e.Parameters);
        };
    }
}

Зарегистрируйте модуль так:

builder.RegisterModule<NamedParameterResolutionModule<IListSerializer>>();

Компоненты получат сериализатор в зависимости от имени параметра конструктора:

class SomeComponent : ...
{
    public SomeComponent(IListSerializer checkBoxSerializer) { ...
}
...