Los Techies : Blogs about software and anything tech!

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.

Kick It on DotNetKicks.com
Posted Jan 25 2010, 06:00 AM by Keith Dahlby

Comments

Chris Tavares wrote re: Extension Methods on Types You Own?
on 01-25-2010 1:06 PM

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.

Ravi Terala wrote re: Extension Methods on Types You Own?
on 01-25-2010 1:49 PM

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.

Twitter Trackbacks for Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software [lostechies.com] on Topsy.com wrote Twitter Trackbacks for Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software [lostechies.com] on Topsy.com
on 01-25-2010 7:42 PM

Pingback from  Twitter Trackbacks for                 Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software         [lostechies.com]        on Topsy.com

uberVU - social comments wrote Social comments and analytics for this post
on 01-26-2010 12:14 AM

This post was mentioned on Twitter by lostechies: blogged: Extension Methods on Types You Own?: It's no secret that I'm a fan of using extension methods to make

cod... http://bit.ly/5gFwmb

Keith Dahlby wrote re: Extension Methods on Types You Own?
on 01-26-2010 9:31 AM

@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 wrote re: Extension Methods on Types You Own?
on 01-26-2010 11:32 AM

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

Keith Dahlby wrote re: Extension Methods on Types You Own?
on 01-26-2010 9:01 PM
Twitter Trackbacks for Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software [lostechies.com] on Topsy.com wrote Twitter Trackbacks for Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software [lostechies.com] on Topsy.com
on 01-27-2010 10:09 AM

Pingback from  Twitter Trackbacks for                 Extension Methods on Types You Own? - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software         [lostechies.com]        on Topsy.com

DCam wrote re: Extension Methods on Types You Own?
on 01-31-2010 7:35 PM

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: intwoplacesatonce.com/.../in-c-3-5-interface-extension-methods-mixin

Weekly Link Post 130 « Rhonda Tipton’s WebLog wrote Weekly Link Post 130 &laquo; Rhonda Tipton&#8217;s WebLog
on 01-31-2010 8:33 PM

Pingback from  Weekly Link Post 130 « Rhonda Tipton’s WebLog

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems