Extension Methods on Types You Own?

It’s no secret that I’m a fan of using extension methods to make
code more concise and expressive. This is particularly handy for
enhancing APIs outside of your control, from the base class library to
ASP.NET MVC and SharePoint. However, there are certain situations where
it might be useful to use extension methods even though you have the
option to add those methods to the class or interface itself. Consider
this simplified caching interface:

public interface ICacheProvider
{
T Get<T>(string key);
void Insert<T>(string key, T value);
}

And a simple application of the decorator pattern to implement a cached repository:

public class CachedAwesomeRepository : IAwesomeRepository
{
private readonly IAwesomeRepository awesomeRepository;
private readonly ICacheProvider cacheProvider;

public CachedAwesomeRepository(IAwesomeRepository awesomeRepository, ICacheProvider cacheProvider)
{
this.awesomeRepository = awesomeRepository;
this.cacheProvider = cacheProvider;
}

public Awesome GetAwesome(string id)
{
var awesome = cacheProvider.Get<Awesome>(id);
if(awesome == null)
cacheProvider.Insert(id, (awesome = awesomeRepository.GetAwesome(id)));
return awesome;
}
}

So far, so good. However, as caching is used more often it becomes
clear that there’s a common pattern that we might want to extract:

    T ICacheProvider.GetOrInsert<T>(string key, Func<T> valueFactory)
{
T value = Get<T>(key);
if(value == default(T))
Insert(key, (value = valueFactory()));
return value;
}

Which would reduce GetAwesome() to a single, simple expression:

    public Awesome GetAwesome(string id)
{
return cacheProvider.GetOrInsert(id, () => awesomeRepository.GetAwesome(id));
}

Now I just need to decide where GetOrInsert() lives. Since I control ICacheProvider,
I could just add another method to the interface and update all its
implementers. However, after starting down this path, I concluded this
was not desirable for a number of reasons:

  1. The implementation of GetOrInsert within each cache provider was essentially identical.
  2. Tests using a mocked ICacheProvider now needed to know if the code
    under test used GetOrInsert() or Get() + Insert(), coupling the test
    too tightly to the implementation. Furthermore, natural tests along the
    lines of “Should return cached value” and “Should insert value from
    repository if not cached” were replaced with a single
    implementation-specific test: “Should return value from GetOrInsert”.
  3. Most importantly, I came to realize that GetOrInsert() really just
    isn’t something that a cache does, so why should it be part of the
    interface?

So instead I have a handy GetOrInsert() extension
method (conversion is left as an exercise for the reader) that I can
use to clean up my caching code without needing to change any of my
cache providers or tests for existing consumers.

The question is really analogous to whether or not Select() and Where() should be part of IEnumerable<T>. They are certainly useful ways to consume the interface, just as GetOrInsert() is, but they exist outside of what an IEnumerable<T> really is.

Related Articles:

About Keith Dahlby

I'm a .NET developer, Git enthusiast and language geek from Cedar Rapids, IA. I work as a software guru at J&P Cycles and studied Human-Computer Interaction at Iowa State University.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.tavaresstudios.com Chris Tavares

    One could argue you get the same benefits of abstraction and you don’t have the question of “where does this live” if you use an abstract base class instead of an interface.

    How often is something going to be an ICacheProvider and something else as well? I personally lean towards using interfaces for orthogonal concerns and base classes for domain concerns.

  • http://rterala.blogspot.com Ravi Terala

    For things like caching that you mentioned, I don’t ever write it as CachedAwesomeRepository anymore. I wrote a generic caching interceptor, which you can write through Unity/Castle to any interface/virtual class that needs caching abilities. Caching policy and differentiating arguments, etc are configured through an external configuration file.

    This helped me configure and add caching to production bits when I want with out changing single line of code or recompiling.

  • http://www.lostechies.com/members/dahlbyk/default.aspx Keith Dahlby

    @Chris ~ An abstract base class would certainly work in this case – I’ll try to think of an example where it wouldn’t. I’m not overly concerned by the question “where does this live” given IDE tools to Go To Definition, Find References, etc., but I’m sure that varies across teams with varying degrees of comfort with extension methods.

    @Ravi ~ How do you handle different caching policies (sliding expiration, absolute expiration, etc) per interface/method with that approach? Sounds like a promising approach.

  • Anon

    Ravi…do you have any examples or can point to any articles on how to implement this? thanks.

  • http://www.lostechies.com/members/dahlbyk/default.aspx Keith Dahlby
  • http://intwoplacesatonce.com DCam

    Seems like a good application of extension methods. Extension methods offer functional abstraction: pulling out common behaviour and putting it somewhere it can be reused or reapplied as appropriate. So, now you can GetOrInsert on any ICacheProvider, no matter what type of ICacheProvider you’re talking about. Brilliant.

    I ran in to a similar situation last year, but didn’t put as much clear though in to why it makes sense this way: http://intwoplacesatonce.com/2009/12/in-c-3-5-interface-extension-methods-mixin/