Thursday, 12 August 2010

Enum Replacement Pattern

I'm sure you've come accross the feeling that although they are handy at first enums have limitations when it comes to OOP.

Here is an implementation that gives you the goodness of enums, having a set of named items that identify things, and adds encapsulation of functionality which I've always felt enums lack.

Lets start off with a simple implementation that represents a time interval (Months and Weeks) that we will use in our domain.




This is simple enough, easy to write and easy to use. However things quickly get more complicated once you start to use this enum. Lets say that we now need to be able to create a TimeSpan that represents one of these intervals from a given DateTime. 



The code does not smell great. It violates single resposibility for a start. It also it not very object orientated, living in its own static class like that. Lets see what can we do to improve this situation.

I'm going to approach this problem by avoiding enums all together. First we create a class that represents the basic concept:



Quite a bit more code for not much benefit at this stage but lets talk about what's happening here.

First notice the private constructor. Only this class can create instances of itself. So the two public static readonly fields are the only two instances of this class ever. This means we can use the standard implmentation of == to compare variables that hold these values. Just like an enum




So far this is just like what we had before. However it gets more interesting when we want to add the functionality for computing offsets from a given date. Because this is a class we can use standard OOP tricks like polymorphisim to encapsulte the functionality.



So now I've made DurationInterval abstract and created two subclasses. Each one encapsulates one way of calculating the date offset. I have had to make the constructors of the Subclasses public. This brakes some of the encapsulation and introduces the possibility that someone could new up an instance on MonthInterval breaking the control over the number of instances in the AppDomain.

We could remedy this be moving the subclasses into DurationInterval and although I think that is kind of cool I try to avoid nested classes in production code. There is an even better solution but I'll save that for the next post on this subject.

In the mean time lets take a step back and see what we've created. Our GetDateFrom method from before would now look like this.



All the calculation is palmed off the the interval itself. At this point we could go and inline all calls to this method as its trivial and its implementation reads better than the method call would.

Hope you found this useful. In future posts I'll show how to make this work properly with serialization, across the wire and to the DB.

Any comments, questions or suggestions please post them bellow.


Share and enjoy.

2 comments:

  1. I like the nested class idea. Why don't you like nesting classes in production code ? I'd rather not expose the design to potentially unexpected results and live with the nested class. What is the alternative you mentioned?

    ReplyDelete
  2. Looking at microsoft's guidance on this again I now think this is exactly where nested types should be used.

    The alternative I mentioned is to use one class that takes a delegate to implement the functionality that changes. I will do a full post on this.

    ReplyDelete