Dependency Injection Patterns

Greetings to my blog readers!

This is a short post to review two dependency injection (DI) design patters: Constructor Injection and Property Injection. These two DIs are the easiest and the most commonly used patterns. There are other DI patters: Ambient Context and Method Injection. Personally I find them rather involved, and will skip them for now. Most of the background material for this post is gathered from Mark Seemann’s Dependency Injection in .Net. I highly recommend this book to anyone interested in DI patters.

Constructor Injection

Constructor injection is the most widely used DI pattern, and is the default patter for most of DI. It works by making all callers of a class to provide the dependency/ies as its public constructor parameter/s. The default or parameter-less constructor is never provided (remember, in C# if you provide your own constructor, the default one is scrapped). As you may have seen in my previous blog post, quite often the dependencies are supplied as interfaces. Here, any class that implements a particular interface can act as the required dependency. However, the passed dependency can be an instance of any object, so it does not have to be an interface.

If you examine the displayed code (i.e. not the pdf file code) from my previous blog on lines 15 through 20, you will see that I have added the guard clause to ensure the dependency is not null. This is an important point, since passing a null to a constructor expecting a reference or a nullable value type will compile, but won’t work. Also, marking the dependency as private and readonly, guarantees its integrity. The clear benefit of constructor DI is the separation of responsibilities, as well as improved code maintainability. If the dependency is meant to be optional, then the property injection could be used instead.

Property Injection

Property injection DI pattern (also known as a Setter Injection) is closely related to the constructor injection. This pattern is used when the dependency in a class is optional, and there may be a local to the class default that can be used instead. If the calling class needs to override the default behavior, it provides its own dependency by setting a public or an internal property. The default behavior is specified in the constructor of the dependent class (i.e. BasicInterpolation.ConsoleWriter). However, to decouple it Output class from interpolation code, I could have added a console-based subclass to the Output class itself.

Below is an example of a property injection DI pattern. I have changed the UserOutput.Output class from the interpolation code to now use this pattern. Note that I am keeping the guard clause to ensure the passed dependency is not null. However, I can no longer have the property as readonly. Also, we allow the property to be set only once:

namespace UserOutput
{
    #region GENERIC_OUTPUT
    internal interface IWriter
    {
        void Write(string message);
    }

    internal class Output
    {
        private IWriter outputwriter;

        public IWriter writer 
        {
            get
            {
                if (this.outputwriter == null)
                {
                    // provide with a default writer
                    this.outputwriter = new BasicInterpolation.ConsoleWriter();
                }
                    return this.outputwriter;
                
            }
            
            set
            {
              if (value == null)
              {
                  throw new ArgumentNullException("Null writer interface.");
              }

              if (this.outputwriter!=null)
              {
                  // writer can only be set once
                  throw new InvalidOperationException();
              }

              this.outputwriter = value;
            }
        }

    }
    #endregion
}

Conclusion

In the Design Patters world, the constructor injection is the simplest pattern. In simplicity, it is followed by the property injection pattern. The latter can be used when a default behavior is required.