torsdag den 4. december 2014

Dependency Injection in a Nutshell

Issue

How to explain in a nutshell how dependency injection makes our code more robust, simple and more easy to maintain.

Case

I have a class that returns a list of user objects loaded from an XML-file.

 public class OpusRepository : IOpusRespository  
 {  
   public virtual List<OpusUser> Users()  
   {  
     // Read file from static path  
     // Get users from file  
     // return list of users  
   }  
 }  

The code that uses the IOpusRepository gets an instance of the OpusRepository class by calling a custom class that creates an instance of the OpusRepository.
It looks like this
 public static class IoC  
 {  
   public static T Get<T>() where T : class  
   {  
     if (typeof (T) == typeof (IOpusRespository))  
       return new OpusRepository() as T;  
     return default(T);  
   
   }  
 }  


All code that needs the IOpusRepository needs to call this static Get<T> method.
A class that uses the interface could look like this
 public class MyClass  
 {  
   private readonly IOpusRespository _opusRespository;  
   
   public MyClass() : this(IoC.Get<IOpusRespository>())  
   {  
   }  
   
   public MyClass(IOpusRespository opusRespository)  
   {  
     if (opusRespository == null) throw new ArgumentNullException("opusRespository");  
     _opusRespository = opusRespository;  
   }  
 }  

The parameter less constructor initializes the opusRespository by calling the Get<T> method.
Now this class can use, without knowing anything about the OpusRepository class, get alle the OPUS users. No knowledge about where the users come from are needed. This is the essense of what we call dependency injection.


The case is now this; for some reason the XML-file grows to 500 MB and takes more than a second to load. GetUsers() is heavily called and we need to code some kind of caching.
This needs to be created without effecting the created unittests and the overall test of the solution. As little code as possible needs to be affected.

Solution

This is where the dependency injection shows its power. By creating a new class with caching and changing the Get<T> method I can easily substitute the executed code with my new class without changing a single line in the critical part of the code.
The new class with caching looks like this

 public class OpusRepositoryCachedUsers : OpusRepository  
 {  
   public override List<OpusUser> Users()  
   {  
     var users = (List<OpusUser>)System.Web.HttpContext.Current.Cache["OpusRepositoryCachedUsers"];  
   
     if (users != null)  
       return users;  
         
     users = base.Users();  
   
     System.Web.HttpContext.Current.Cache.Add("OpusRepositoryCachedUsers", users, null, DateTime.Now.AddMinutes(10), System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);  
     return users;  
   }  
 }  

The class inherits the original OpusRepository and overrides the Users() method. It accesses the caching object and calls only Users() method on the base class if the list is not found in cache.

The change to get Get<T> method looks like this
 public static class IoC  
 {  
   public static T Get<T>() where T : class  
   {  
     if (typeof (T) == typeof (IOpusRespository))  
       return new OpusRepositoryCachedUsers() as T;  
     return default(T);  
   
   }  
 }  

I have now implemented a caching layer without affecting any unittests that might use the OpusRepository and havnt changed any code in the OpusRepository and therefore only needs to test the caching layer.
This is the true power of dependency injection.

Ingen kommentarer:

Send en kommentar