6
votes

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 ILogger 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...

3
Why do you not want to use a static factory pattern?Tim Lloyd
Ctor injection, etc, is very useful for varying implementations for testing, etc, on an individual basis, but do you really need this for logging? Do you really need the ability to vary logging implementation at the instance level? The static factory pattern will still give you the ability to vary the overall logging implementation which is usually what is required.Tim Lloyd
The implementation of what Logger to return needs to be dynamic. Specifically Logger.Instance should be different depending on the context (ie WCF operation, WPF client, SL client, etc). I guess I could do a Service Locator inside .Current, but that's kind of an anti-pattern as I understand it...Jeff
It would appear you need to vary the logging according to the runtime environment (e.g. WPF Client, SL Client, etc) rather than at the level of type instances. I think a static factory used in conjunction with DI at this point fits the bill nicely.Tim Lloyd

3 Answers

1
votes

In your case i would use property injection.

Property injection can be switched to mandatory as explained here: http://www.mail-archive.com/[email protected]/msg08163.html

3
votes

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.

0
votes

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.