4

Prior to getting into DI I was quite a fan of using a so-called enum class (or strong enum in my head), where enumerations are turned into classes but set up to be used in a similar way to enums. This enables logic that really belongs to a specific enum to be encapsulated in the correct place and prevents lots of mess in the code base.

An example is the one found here http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/

Once you bring DI into the equation though it becomes problematic because of the reliance on static variables.

Is there any way of continuing to support this pattern and also use DI?

Edit: Here is an example of a type that is problematic if I want to inject some thing new into EmployeeType. I can't use the container due to the static variables.

public class EmployeeType : Enumeration
{
    public static readonly EmployeeType Manager 
        = new ManagerType (0, "Manager");
    public static readonly EmployeeType Servant 
        = new EmployeeType(1, "Servant");
    public static readonly EmployeeType AssistantToTheRegionalManager 
        = new EmployeeType(2, "Assistant to the Regional Manager");

    private EmployeeType() { }
    private EmployeeType(int value, string displayName) : base(value, displayName) { }
}

public class ManagerType : EmployeeType
{
}
Ian1971
  • 3,666
  • 7
  • 33
  • 61
  • What's the advantage of this pattern over regular inheritance or the strategy pattern (neither of which have this problem)? – Rik Nov 18 '13 at 11:08
  • I suppose familiarity/usability and ease of migration from an enum to an enum class (i.e. used identically). – Ian1971 Nov 18 '13 at 11:24
  • Please show an example of what you are doing and where this is problematic in conjunction with DI. – Steven Nov 18 '13 at 11:50
  • 3
    why would you need DI for this when it is essentially hard-coded? You don't use DI with a standard enum, correct? – Moho Nov 18 '13 at 12:26
  • This isn't my actual code, but sticking with this fake example, lets say I later wanted to inject some factory or other because I needed to expose something in the EmployeeType interface. – Ian1971 Nov 18 '13 at 12:35
  • why don't you do a real enum with attributes? – Daniel A. White Nov 18 '13 at 13:23
  • @daniel - can you ellaborate on that? – Ian1971 Nov 18 '13 at 13:26
  • http://stackoverflow.com/questions/1799370/getting-attributes-of-enums-value – Daniel A. White Nov 18 '13 at 13:42
  • Let's face it, if you need to inject dependencies into `EmployeeType` or its derived classes, you can't use this method. In that case you can't store those classes in a static field. – Steven Nov 18 '13 at 16:25
  • I guess at the point you realise you need to inject something then you are faced with a "replace enum class with strategy pattern" refactoring (I may have just made that up). – Ian1971 Nov 18 '13 at 16:51

3 Answers3

3

I think you can use Strategy pattern here. You can keep your enum type or use any type than can help you to identify the strategy (I will use string in my example). Then implement a strategy for each value.

interface IEmployeeHandler
{
    string EmployeeType { get; }
    void Handle(Employee employee);
}

class ManagerHandler : IEmployeeHandler
{
    public ManagerHandler()
    {
        EmployeeType = "Manager";
    }

    public string EmployeeType { get; private set; }
    public void Handle(Employee employee) { }
}

class ServantHandler : IEmployeeHandler
{
    public ServantHandler()
    {
        EmployeeType = "Servant";
    }

    public string EmployeeType { get; private set; }
    public void Handle(Employee employee) { }
}

Since DI containers can inject multiple implementations of an interface, you can write a strategy provider class like this:

class EmployeeStrategyProvider
{
    private readonly IEmployeeHandler[] _employeeHandlers;

    public EmployeeStrategyProvider(IEmployeeHandler[] employeeHandlers)
    {
        _employeeHandlers = employeeHandlers;
    }

    public IEmployeeHandler GetStrategy(string employeeType)
    {
        return _employeeHandlers.FirstOrDefault(item => item.EmployeeType == employeeType);
    }
}

You can use this class to get the correct strategy depending on the employee type. This is how I implement Strategy pattern with DI containers. It can use some improvements though, I would be happy to hear some suggestions.

Ufuk Hacıoğulları
  • 37,978
  • 12
  • 114
  • 156
  • Is there a way of still keeping the EmployeeType.Manager enum style notation. Have you ever combined the above with a singleton (see B3ret's answer)? Is this a good idea? – Ian1971 Nov 18 '13 at 15:34
  • @Ian1971 It can be done but I think it's unnecessary. You will still need to create an instance of that 'enum type' using some data in your code. Just include that data in your strategy interface and you can make it work without it without all those static instance. – Ufuk Hacıoğulları Nov 18 '13 at 15:46
  • ok, makes sense. I like your implementation. So I think perhaps the answer to my question "is the enum class pattern incompatible with di?" is really no, but it is better to replace it with the strategy pattern. – Ian1971 Nov 18 '13 at 15:53
  • @Ian1971 I can't say it's incompatible. They can certainly work together but I think it's redundant. Although I am no authority here, you can make the best decision for your case. – Ufuk Hacıoğulları Nov 18 '13 at 20:43
2

I've been using this pattern myself for some time but had never considered the need to inject a service into it. But hey it should be possible right?

One solution is to delay the creation of any enumerations that depend on a service instance until they are required - everything else still looks and acts like a regular static type.

Here's the EmployeeType class with the addition of the delayed instantiation of the Manager that depends on IBonusService:

public class EmployeeType : Enumeration
{
    public static Func<IBonusService> BonusService { private get; set; }

    private static EmployeeType _manager = null;
    public static EmployeeType Manager { 
        get 
        {
            if (_manager == null) _manager = new ManagerType(BonusService());
            return _manager;
        } }

    public static readonly EmployeeType Servant
        = new EmployeeType(1, "Servant");
    public static readonly EmployeeType AssistantToTheRegionalManager
        = new EmployeeType(2, "Assistant to the Regional Manager");
    private EmployeeType(int value, string displayName) :
       base(value, displayName) { }

    public virtual decimal BonusSize { get { return 0; } }

    private class ManagerType : EmployeeType
    {
        private readonly IBonusService service;
        public ManagerType(IBonusService service) : base(0, "Manager")
        {
            this.service = service;
        }

        public override decimal BonusSize {
            get { return this.service.currentManagerBonus; } }
    }
}

And you configure the dependencies within the composition root:

[Test]
public void EmployeeType_Manager_HasBonusService()
{
    Container container = new Container();
    container.Register<IBonusService, BonusServiceStub>();
    EmployeeType.BonusService = () => container.GetInstance<IBonusService>();

    BonusServiceStub.constructed = false;
    container.Verify();
    //Verify has ensured the container can create instances of IBonusService
    Assert.That(BonusServiceStub.constructed, Is.True);

    EmployeeType manager = EmployeeType.Manager;
    Assert.That(manager.BonusSize == 999m);
}


public class BonusServiceStub : IBonusService
{
    public static bool constructed = false;

    public BonusServiceStub() { constructed = true; }

    public decimal currentManagerBonus { get { return 999m; } }
}

The reason for specifically injecting a Func<IBonusService> instead of IBonusService is so that the lifetime scope (as well as the decoration etc.) of the service is managed within the composition root and remains independent of the consumer.

qujck
  • 14,388
  • 4
  • 45
  • 74
  • That is pretty nice. One thing I wonder is does this EmployeeType.BonusService = () => container.GetInstance(); make it a service locator (i.e. anti-pattern) or is it ok because the definition of the Func is in the composition root even though it might be instantiated later? – Ian1971 Nov 19 '13 at 12:27
  • @Ian1971 all of the configuration is in the composition root so it's not a service locator. If you don't configure the `EmployeeType` correctly you'll get a `NullReference` error when trying to get a `Manager` - the bug is in the composition root. It occurred to me after posting that you can get a fresh instance of `Manager` for each call and not cache at all - ensuring you always get the current bonus amount. – qujck Nov 19 '13 at 13:07
0

You could use a Singleton for your enum class.

B3ret
  • 597
  • 5
  • 19
  • That did occur to me but it wasn't clear to me how I could create that singleton and get the dependency injected, but thinking about it some more I suppose if the singleton was created in the CompositionRoot then it might work out ok. – Ian1971 Nov 18 '13 at 12:46
  • 1
    I guess I would need a singleton for each subtype of the enum class if it had any. e.g. ManagerType above – Ian1971 Nov 18 '13 at 12:49
  • Yes unfortunately you'd have to implement the singleton for each subclass. – B3ret Nov 19 '13 at 13:58