Andrew Stopford's Weblog

poobah

Sponsors

News

Articles

Family

Old Blogs

Caching and SRP pt2

In the comments of the previous example Petar suggested a refactroring to reduce the coupling between object using the cache and the cache it's self, he proposed using generics and it works just fine in the IOC, let's take a look. Starting with the interface

   1:  public interface ICache<T>
   2:  {
   3:    void Add(T res, string key);
   4:    T Get(string key);
   5:     void Remove(string key);
   6:  }

now the cache class

   1:  public class Velocity<T> : ICache<T>
   2:      {
   3:          private const string CACHE = "MyCache";
   4:          
   5:          public void Add(T res, string key)
   6:          {
   7:              var CacheCluster1 = new CacheFactory();
   8:              var Cache1 = CacheCluster1.GetCache(CACHE);
   9:              Cache1.Add(key, res);
  10:          }
  11:   
  12:          public T Get(string key)
  13:          {
  14:              var CacheCluster1 = new CacheFactory();
  15:              var Cache1 = CacheCluster1.GetCache(CACHE);
  16:   
  17:              return (T) Cache1.Get(key);
  18:          }
  19:   
  20:          public void Remove(string key)
  21:          {
  22:              var CacheCluster1 = new CacheFactory();
  23:              var Cache1 = CacheCluster1.GetCache(CACHE);
  24:   
  25:              Cache1.Remove(key);
  26:          }
  27:      }

The object using the cache now looks like the following.

   1:  public class BasketActions
   2:  {
   3:    private ICache<Basket> _cache;
   4:   
   5:    public BasketActions(ICache<Basket> cache)
   6:    {
   7:       _cache = cache;
   8:    }
   9:   
  10:    public Basket Get(int basketid)
  11:    {
  12:      var cacheValue = cache.Get(basketid);
  13:     if(cacheValue == null)
  14:     {  
  15:       var res = GetBasket(basketid);
  16:       if(res != null)
  17:       {
  18:          cache.Add(res, cachekey);
  19:       }
  20:       return res; 
  21:      }
  22:      return cacheValue;
  23:    }
  24:   
  25:    private Basket GetBasket(int basketid)
  26:    {
  27:      ...
  28:    }
  29:  } 

and finally we change our IOC call to the following.

   1:  IOC.Register<ICache<Basket>, Velocity<Basket>>();
Posted: Sep 16 2008, 05:20 PM by andrewstopford | with 4 comment(s)
Filed under:

Comments

Dew Drop - September 17, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - September 17, 2008 | Alvin Ashcraft's Morning Dew

# September 17, 2008 9:24 AM

Petar Petrov said:

Hi again.

I like this version much more but still I don't like this line :

var cacheValue = cache.Get(basketid);

It won't compile ;) - we need basketid.ToString() but that isn't the main problem.

Suppose we add another class say Car and we implement the same cache strategy for this class. What will happen when we call the Get method with "1". Is it a carID or a basketID ? When we perform :

return (T) Cache1.Get(key);

and we have a Basket in the cache with basketID = 1. If we try to retrieve a Car with carID = 1 the will fail !

So we need a way to distinguish carIDs and backetIDs.

Here's my proposal

   public interface ICache<T> where T : ICachable

   {

       void Add(T res);

       T Get(string key);

       void Remove(string key);

   }

   public interface IIdentifiable

   {

       int ID { get; set; }

       string ClassName { get; }

   }

   public interface ICachable

   {

       string CacheID { get; }

   }

   public class Velocity<T> : ICache<T> where T : ICachable, IIdentifiable

   {

       private static readonly string CACHE = Cache.DefaultCache;

       private CacheFactory _cacheCluster = new CacheFactory();

       public void Add(T res)

       {

           var Cache1 = _cacheCluster.GetCache(CACHE);

           Cache1.Add(res.CacheID, res);

       }

       public T Get(string key)

       {

           var Cache1 = _cacheCluster.GetCache(CACHE);

           return (T)Cache1.Get(key);

       }

       public void Remove(string key)

       {

           var Cache1 = _cacheCluster.GetCache(CACHE);

           Cache1.Remove(key);

       }

   }

   public abstract class ObjectActions<T> where T : ICachable, IIdentifiable, new()

   {

       private ICache<T> _cache;

       public ObjectActions(ICache<T> cache)

       {

           _cache = cache;

       }

       public T Get(int objectID)

       {

           T b = new T() { ID = objectID };

           var cacheValue = _cache.Get(b.CacheID);

           if (cacheValue == null)

           {

               var res = GetBasket(objectID);

               if (res != null)

               {

                   _cache.Add(res);

               }

               return res;

           }

           return cacheValue;

       }

       protected abstract T GetBasket(int objectID);

   }

   public class BasketActions : ObjectActions<Basket>

   {

       public BasketActions(ICache<Basket> cache)

           : base(cache)

       {

       }

       protected override Basket GetBasket(int basketid)

       {

           return new Basket() { ID = basketid };

       }

   }

   public class Basket : IIdentifiable, ICachable

   {

       #region IIdentifiable Members

       public int ID { get; set; }

       public string ClassName

       {

           get { return @"Basket"; }

       }

       #endregion

       #region ICachable Members

       public string CacheID

       {

           get

           {

               if (ID <= 0)

               {

                   throw new ArgumentOutOfRangeException("value", "The ID must be greated then zero.");

               }

               return ClassName + ID;

           }

       }

       #endregion

   }

   public class Car : IIdentifiable, ICachable

   {

       #region IIdentifiable Members

       public int ID { get; set; }

       public string ClassName

       {

           get { return @"Car"; }

       }

       #endregion

       #region ICachable Members

       public string CacheID

       {

           get

           {

               if (ID <= 0)

               {

                   throw new ArgumentOutOfRangeException("value", "The ID must be greated then zero.");

               }

               return ClassName + ID;

           }

       }

       #endregion

   }

what do you think ?

# September 18, 2008 8:37 AM

Andy Stopford said:

Hi Petar,

I see your point and thanks for posting your code up, never done pair programming in a blog post before :)

One other way I an think of is the use of the Velocity regions, you could partion certain objects by region id and use that as a marker in the code for certain objects. Your solution is more generic however.

Thanks again for taking the time to share your thoughts.

# September 18, 2008 7:00 PM

Jeremy Wiebe said:

I'm not sure which IoC you are using but with Unity you can register the ICache<T> as an open generic type and then resolve any number of different closed types using the single registration. (I hope I have that terminology correct.  :-|  )

So instead of:

IOC.Register<ICache<Basket>, Velocity<Basket>>();

You can do:

IOC.Register<ICache<>, Velocity<>>();

Now anywhere you use ICache<T> the container will resolve it to Velocity<T>.

# September 18, 2008 10:26 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)