Menu

Sunday, 16 December 2012

Generic class factory in C#

Good object oriented design prefers has-a instead of is-a relationships. Dependency injection is a technique which achieves has-a relationships and still maintains unit testability. Dependency injection is a design pattern which injects flexibility into the relationships, or dependencies which occur between objects in object oriented software. Using constructor injection, the snippet below shows how the the wheel is injected into the Car class. The Car class does not create the Wheel object, rather is given  the Wheel object and thus supporting a has-a relationship.

namespace SoftwareSprint.DemoLogic
{
    public interface IWheel
    {
        double Pressure { get; set; }

        void Turn();
    }
}
namespace SoftwareSprint.DemoLogic
{
    public class Wheel : IWheel
    {
        public double Pressure { get; set; }

        public void Turn()
        {
            throw new System.NotImplementedException();
        }
    }
}
namespace SoftwareSprint.DemoLogic
{
    public class Car
    {
        private readonly IWheel localWheel = null;

        public Car(IWheel wheel)
        {
            this.localWheel = wheel;
        }

        public IWheel LocalWheel
        {
            get { return this.localWheel; }
        }

        public void Drive()
        {
            this.localWheel.Turn();
        }
    }
}

However, dependency injection can be easily overused. Firstly, with overuse it becomes challenging for the developer to trace all of the injected dependencies in the code. Secondly, if for example, many dependencies are injected into a constructor and the constructor is overloaded, most likely all of the dependencies in the constructor parameter list will need to be copied to the new overloaded constructor.

Generic class factory

A generic class factory changes the implementation of dependency injection while still supporting the design pattern and retaining dependency inject-ability. This means, as in the Car class, the constructor would be freed of injected objects. The generic class factory provisions a object dispenser. The dispenser is responsible for administering the injected objects. If the dispenser has not been set, then the factory will create the object which the factory consumer seeks.

namespace SoftwareSprint.Helper
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    [SuppressMessage("Gendarme.Rules.Design.Generic", "DoNotDeclareStaticMembersOnGenericTypesRule", Justification = "Will only be used in Unit testing")]
    public class GenericClassFactory<T, TClass>
        where TClass : T
    {
        [SuppressMessage("Microsoft.Design", "CA1000", Justification = "Will only be used in Unit testing")]
        public static Func<object[], T> Dispenser { get; set; }

        public T CreateInstance(params object[] args)
        {
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }

            if (Dispenser != null)
            {
                return Dispenser(args);
            }

            return (T)Activator.CreateInstance(typeof(TClass), args);
        }

        public T CreateInstance()
        {
            if (Dispenser != null)
            {
                return Dispenser(new object[] { });
            }

            return (T)Activator.CreateInstance(typeof(TClass), new object[] { });
        }
    }
}

The consumer of the generic class factory creates a new instance of the factory and calls the factory's CreateInstance method. The CreateInstance method will return the dispenser object if been set. If the dispenser has not been set, a new object is created. The constructor injection is removed from the CarImproved class.

namespace SoftwareSprint.DemoLogic
{
    using SoftwareSprint.Helper;

    public class CarImproved
    {
        private readonly IWheel localWheel;

        public CarImproved()
        {
            GenericClassFactory<IWheel, Wheel> genericClassFactory = new GenericClassFactory<IWheel, Wheel>();
            this.localWheel = genericClassFactory.CreateInstance();
        }

        public void Drive()
        {
            this.localWheel.Turn();
        }
    }
}

Naturally, unit tests inject stubbed or mocked objects. The unit test will set the dispenser of the factory with the stubbed and mocked object. In unit test below, the dispenser is set with the stubbed Wheel object in the set up of the test fixture.

namespace SoftwareSprint.UnitTest
{
    using NSubstitute;

    using NUnit.Framework;

    using SoftwareSprint.DemoLogic;
    using SoftwareSprint.Helper;

    [TestFixture]
    public class CarTests
    {
        private IWheel wheel = null;

        [SetUp]
        public void Setup()
        {
            this.wheel = Substitute.For<IWheel>();
            GenericClassFactory<IWheel, Wheel>.Dispenser = (args) => this.wheel;
        }

        // Stub test
        [Test]
        public void Drive_DoesNotThrowException_ReturnOk()
        {
            CarImproved carImproved = new CarImproved();
            carImproved.Drive();
        }

        // Mock test
        [Test]
        public void Drive_WheelIsTurned_ReturnOk()
        {
            CarImproved carImproved = new CarImproved();
            carImproved.Drive();

            this.wheel.Received().Turn();
        }

    }
}

Employing the generic class factory improves the maintainability of the software while still supporting the design pattern dependency injection. The code remains tidy. Unit test have more control over dispensing stubbed and mocked objects. Finally, the factory method pattern is also supported in the generic class factory.

Code can be downloaded from GitHub
https://github.com/softwaresprint/genericclassfactory

No comments:

Post a Comment