I am working on a colleague to refactor the following code, I am trying to turn this into a teaching experience as well.
Original Code
public class WidgetRepository
{
public Widget GetWidget(int id)
{
Widget widget;
if (HttpContext.Current != null)
{
widget = (Widget)HttpContext.Current.Cache["widget_" + id];
}
if (widget == null)
{
widget = GetWidgetImpl(id); // some db call
if (HttpContext.Current != null)
{
HttpContext.Current.Cache["widget_" + id] = widget;
}
}
return widget;
}
}
The most obvious thing that sticks out is that you have your repository knowing about ASP.NET concepts, which is particularly bad as this repository is used by non-ASP.NET code in some scenarios.
Refactored Code
I decided to refactor the code as follows:
public class WidgetRepository
{
private readonly ICacheProvider _cacheProvider;
public WidgetRepository(ICacheProvider cacheProvider)
{
_cacheProvider = cacheProvider;
}
public Widget GetWidget(int id)
{
var widget = _cacheProvider.Get<Widget>("widget_" + id);
if (widget == null)
{
widget = GetWidgetImpl(id); // some db call
_cacheProvider.Add("widget_" + id, widget);
}
return widget;
}
}
public interface ICacheProvider
{
T Get<T>(string key);
void Add<T>(string key, T item);
}
public HttpCacheProvider : ICacheProvider
{
private readonly Cache _cache;
public HttpCacheProvider(Cache cache)
{
_cache = cache;
}
public T Get<T>(string key)
{
return (T)_cache[key];
}
public void Add<T>(string key, T item)
{
_cache[key] = item;
}
}
public NullCacheProvider : ICacheProvider
{
public T Get<T>(string key)
{
return null;
}
public void Add<T>(string key, T item)
{
}
}
My Justification
I explained the following benefits to the refactored code:
Promotes Open / Close: we can change the caching implementation if we want to use something else in the future (memcached, etc.)
Lower cyclomatic complexity: less if statements
Not mixing ASP.NET technology with data-access technology
Refutations / Concerns
However, I got the following refutations:
We're not going to use a different caching technology in the foreseeable future.
It's more lines of code (therefore it's more complex)
You need to setup the dependencies (complexity is moved, not removed)
There's already tonnes of code that uses
HttpContext.Current
, so this code does not follow the existing (anti-)patternWe're still "using" ASP.NET within data-access code.
My Questions
Personally, I find that the pros outweigh the refutations, however, I would like to be able to address the refutations in order to help educate them.
My questions are:
Am I justified in making the refactoring (assuming we have time allocated for cleaning up code)?
How do I respond to the various refutations?
HttpContext.Current.Cache
toHttpRuntime.Cache
everywhere. This isn't just about changing implementation, it's about being DRY with the information of how the cache is accessed.HttpContext.Current
imposes a thread-local requirement on the code, which may impede future refactoring or design patterns. A pragmatist is also well-informed and know that other pragmatists had been burned by this before, so that this threat is credible (not something remote at all), and will heed the advice. Therefore, a smart YAGNI-ist will go the way of pragmatist and take the same advice too.