Альтернативы инжектора конструктора (замок Виндзор) - PullRequest
6 голосов
/ 09 декабря 2010

Мне нравится внедрение в конструктор для внедрения зависимостей.Он вызывает четкое объявление проблем от типа и помогает с тестируемостью.

Мне нравится внедрение в конструктор, в большинстве местах ...

В качестве примера, где яне нравится.Если у меня есть базовый класс, от которого наследуются многие другие классы, и я хочу, чтобы все эти классы использовали экземпляр моего ILogger (или чего-то еще), и мне не нужна статическая фабрика (Logger.Instance) ... Яя не хочу объявлять конструктор для каждого подкласса, который принимает ILogger.

Итак, я мог бы попросить мой базовый класс объявить регистратор как свойство и сделать его таким образом

public class MyBaseClass 
{
   public ILogger Logger { get; set; }
}

... но

  1. Это не гарантирует мне, что Logger на самом деле вводится и не является нулевым.
  2. Не знаюнапример, иметь ILogger с открытым набором

Итак ... какие еще варианты у меня есть?(Я использую Castle Windsor).

Я думал о создании интерфейса

public interface IInitializable<T>
{
    void Initialize(T instance); 
}

public class MyBaseClass : IInitializable<ILogger>, ...could have other IInitializables too...
{
   protected ILogger Logger { get; private set; }

   public void Initialize(ILogger instance) 
   { 
         Logger = instance;
   }
}

Затем в моем контейнере будет средство, которое автоматически вызывает все реализации IInitializable<T> при построении типа...

Но мне интересно, что думают другие люди, прежде чем идти этим путем ...

Ответы [ 3 ]

3 голосов
/ 09 декабря 2010

Ты слишком усложняешь это. Рекомендуемый и документированный шаблон для внедрения ILogger должен иметь NullLogger.Instance по умолчанию (т.е. шаблон нулевого объекта ) и делать Logger необязательной зависимостью. Нет ничего плохого в общедоступном сеттере для регистратора. Использование пользовательского IInitializable, подобного тому, которое вы показываете, скорее всего только усложнит ситуацию и не принесет никакой реальной ценности.

Я скопирую образец из документации здесь для удобства:

using Castle.Core.Logging;

public class CustomerService
{
   private ILogger logger = NullLogger.Instance;

   public CustomerService()
   {
   }

   public ILogger Logger
   {
      get { return logger; }
      set { logger = value; }
   }

   // ...
}

РЕДАКТИРОВАТЬ: кажется, что на самом деле вопрос заключается в наличии различных реализаций регистратора в зависимости от контекста (который имеет мало общего с исходным вопросом). В этом случае используйте переопределения служб или селекторы обработчиков.

1 голос
/ 09 декабря 2010

В вашем случае я бы использовал инъекцию свойства.

Ввод свойства может быть переключен на обязательный, как описано здесь: http://www.mail-archive.com/castle-project-users@googlegroups.com/msg08163.html

0 голосов
/ 10 декабря 2010

Если ваш базовый класс не зависит от ILogger, вы должны удалить это свойство из базового класса. Таким образом вы сохраните конструктор базового класса в чистоте.

В противном случае вы можете создать фабрику, способную создавать потомков MyBaseClass. Это может выглядеть так:

public interface IMyBaseClassFactory
{
    MyBaseClass CreateNew<TMyBaseClass>() where TMyBaseClass : MyBaseClass;
}

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

public MyBaseClassFactoryImpl : IMyBaseClassFactory
{
    public MyBaseClass CreateNew<TMyBaseClass>()
    {
        // Call your IoC container to get a descendant.
        var instance = ServiceLocator.GetInstance<TMyBaseClass>();

        // Register missing dependencies
        instance.Logger = ServiceLocator.GetInstance<ILogger>();

        return instance;
    }
}

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

При определении свойства для инъекции вам нужно сделать его доступным для чтения / записи. Конечно, вы не хотите, чтобы кто-то случайно сбросил это свойство впоследствии. Без внедрения в конструктор трудно добиться этого с поддержкой времени компиляции. Тем не менее, проверка выполнения выполняется легко:

private ILogger logger;

public ILogger Logger
{
    get { return this.logger; }
    set
    {
        if (this.logger != null)
        {
            throw new InvalidOperationException("Logger has already been set.");
        }

        this.logger = value;
    }
}

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...