开发者

Constructor Injection Alternatives (Castle Windsor)

I like Constructor injection for dependency injection. It forces a clear declaration of concerns from a type and helps with testability.

I like Constructor injection, in most places...

Logging as an example where I do not like it. If I have a base class from which many other classes inherit, and I want all of those classes to use an instance of my ILogger (or whatever), and I don't want a static factory (Logger.Instance)...I don't want to have to declare a constructor on every sub-class that takes an ILogger.

So, I could have my base class declare the logger as a Property and have it injected that way

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

...but

  1. That doesn't assure me that Logger actually gets injected and is not null.
  2. I don't like having ILogg开发者_JS百科er with a public set

So...what other options do I have? (I'm using Castle Windsor).

I've contemplated making an interface

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;
   }
}

Then having a facility on my container that automatically calls all implementations of IInitializable<T> upon type construction...

But I'm wondering what other peoples' thoughts are before I go that route...


You're overcomplicating this. The recommended and documented pattern to inject ILogger is to have NullLogger.Instance as default (i.e. a null object pattern) and make the Logger an optional dependency. There is nothing wrong with having a public setter for the logger. Using a custom IInitializable like the one you show will likely only complicate things and not contribute any real value.

I'll copy the sample from the documentation here for easy reference:

using Castle.Core.Logging;

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

   public CustomerService()
   {
   }

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

   // ...
}

EDIT: it seems the question is actually about having different logger implementations depending on the context (which has little to do with the original question). If that's the case, use service overrides or handler selectors.


In your case i would use property injection.

Property injection can be switched to mandatory as explained here: http://www.mail-archive.com/castle-project-users@googlegroups.com/msg08163.html


If your base class itself is not depending on the ILogger, you should remove the property from the base class. This way you keep the base class constructor clean.

Otherwise you can create a factory that is able to create descendants of MyBaseClass. This might look like this:

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

Now you can create an register an implementation of IMyBaseClassFactory that is able to create new instances and register optional dependencies:

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;
    }
}

Most IoC containers allow you to decorate injectable properties with attributes. The advantage of using a factory however is, that your application will stay completely ignorant of the used IoC framework. As a downside, there's more work to do (creating a factory, injecting that factory instead of the instance itself, and calling the factory to create a new instance).

When defining a injectable property, you need to make it read/write. Of course you don't want anyone to accidentally reset that property afterwards. Without constructor injection it is hard to achieve this with compile time support. However, a runtime check is easily made:

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;
    }
}

I hope this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜