On Software Development: Moving from statics or instances to a DI container

I’ve recently started refactoring a customer’s code base for a working application.  They recognize the need to make their code more extensible and maintainable so I’m helping to massage the existing code into something that they will be able to continue shipping and upgrading for years to come without ending up backed into a corner.

One of my first suggestions was to start eliminating the abundance of static variables in the code base.  In this case, static classes and methods abound, and it looks like it was used as a quick-and-dirty mechanism to provide singleton behavior.  Now I’m not going to go into depth on why an actual singleton might have been better, or the pitfalls of all of these statics.  Write-ups on that kind of thing about in books and on line.

Instead, let’s look at what it means to migrate from a static, an instance or a singleton over to using a DI container, specifically OpenNETCF’s IoC framework.

First, let’s look at a “service” class that exposes a single integer and how we might consume it.

   1: class MyStaticService

   2: {

   3:     public static int MyValue = 1;

   4: }

And how we’d get a value from it:

   1: var staticValue = MyStaticService.MyValue;

Simple enough.  Some of the down sides here are:

  • There’s no way to protect the Field value from unwanted changes
  • To use the value, I have to have a reference to the assembly containing the class
  • It’s really hard to mock and cannot be moved into an interface

Now let’s move that from a static to an instance Field in a constructed class:

   1: class MyInstanceService

   2: {

   3:     public MyInstanceService()

   4:     {

   5:         MyValue = 1;

   6:     }

   7:  

   8:     public int MyValue { get; set; }

   9: }

Now we have to create the class instance and later retrieve the value.

   1: var service = new MyInstanceService();

   2:  

   3: // and at a later point....

   4: var instanceValue = service.MyValue;

We’ve got some benefit from doing this.  We can now control access to the underlying value, making the setter protected or private, and we’re able to do bounds checking, etc.  All good things.  Still, there are downsides:

  • I have to keep track of the instance I created, passing it between consumers or maintaining a reachable reference
  • I have no protection from multiple copies being created
  • The consumer must have a reference to the assembly containing the class (making run-time plug-ins very hard)

Well let’s see what a Singleton pattern buys us:

   1: class MySingletonService

   2: {

   3:     private static MySingletonService m_instance;

   4:  

   5:     private MySingletonService()

   6:     {

   7:         MyValue = 1;

   8:     }

   9:  

  10:     public static MySingletonService Instance

  11:     {

  12:         get

  13:         {

  14:             if (m_instance == null)

  15:             {

  16:                 m_instance = new MySingletonService();

  17:             }

  18:             return m_instance;

  19:         }

  20:     }

  21:  

  22:     public int MyValue { get; set; }

  23: }

And now the consumer code:

   1: var singleTonValue = MySingletonService.Instance.MyValue;

That looks nice from a consumer perspective.  Very clean.  I’m not overly thrilled about having the Instance accessor property, but it’s not all that painful.  Still, there are drawbacks:

  • The consumer must have a reference to the assembly containing the class (making run-time plug-ins very hard)
  • If I want to mock this or swap implementations, I’ll got to go all over my code base replacing the calls to the new instance (or implement a factory).

How would all of this look with a DI container?

   1: interface IService

   2: {

   3:     int MyValue { get; }

   4: }

   5:  

   6: class MyDIService : IService

   7: {

   8:     public MyDIService()

   9:     {

  10:         MyValue = 1;

  11:     }

  12:  

  13:     public int MyValue { get; set; }

  14: }

Note that the class is interface-based and we register the instance with the DI container by *interface* type.  This allows us to pull it back out of the container later by that interface type.  The consumer doesn’t need to know anything about the actual implementation.

   1: // Note that the Services collection holds (conceptually) singletons.  Only one instance per registered type is allowed.

   2: // If you need multiple instances, use the Items collection, which requires a unique identifier string key for each instance

   3: RootWorkItem.Services.AddNew<MyDIService, IService>();

   4:  

   5: // and at a later point....

   6: var diValue = RootWorkItem.Services.Get<IService>().MyValue;

Mocks, implementation changes based on environment (like different hardware) and testing become very easy.  Plug-in and run-time feature additions based on configuration or license level also are simplified.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s