Register Login

 

Welcome to the Coach Factor blog. Here you will find all of our ideas on software development. Subscribe at  http://blog.nventive.net.

# Friday, March 28, 2008

Part 1 and Part 2 of this serie presented the basics and design guidelines for developing great extension methods.

Today, we'll try to show how extension methods can be a great tool to lessen the burden of implementing any given interface.

As an example, let's take a look at Unity for a moment.

IUnityContainer is the main interface of Unity. It acts both as a registry of registered type mappings between an abstract type and a concrete type; and as a Service Locator (Dependency Lookup).

What we're used to

Here's a snippet of the RegisterType method with its different overloads:

 public interface IUnityContainer

{

    //...

    IUnityContainer RegisterType()

      where TTo : TFrom;

    IUnityContainer RegisterType(LifetimeManager lifetimeManager)

      where TTo : TFrom;

    IUnityContainer RegisterType(LifetimeManager lifetimeManager);

    IUnityContainer RegisterType(string name)

      where TTo : TFrom;

    IUnityContainer RegisterType(string name, LifetimeManager lifetimeManager)

      where TTo : TFrom;

    IUnityContainer RegisterType(string name, LifetimeManager lifetimeManager);

    IUnityContainer RegisterType(Type t, LifetimeManager lifetimeManager);

    IUnityContainer RegisterType(Type from, Type to);

    IUnityContainer RegisterType(Type t, string name, LifetimeManager lifetimeManager);

    IUnityContainer RegisterType(Type from, Type to, LifetimeManager lifetimeManager);

    IUnityContainer RegisterType(Type from, Type to, string name);

    IUnityContainer RegisterType(Type from, Type to,

      string name, LifetimeManager lifetimeManager);

}

12 different overloads!!! In total, there are 39 different methods on this interface.

Let's focus on the RegisterType method for a sec. What are the main differences between those overloads?

First and foremost, there's the Generic-strongly-typed version and the loosely-typed version. Then there's the name parameter that allows you to register the same type multiple times with different names. Then there's whether or now you want to specify a lifetimeManager.

If we were to implement that interface and all those RegisterType methods, what would happen? How would we do it? Well, we'd probably chain them all to a single one; the same we do it with chained constructors, right? Which one? The more specific one? Definitely:

 IUnityContainer RegisterType(Type from, Type to, string name, LifetimeManager lifetimeManager);

This is the core method. The one that rules them all. All other overloads are only there to ease usage; at the cost of putting a large burden on the developer. If we want to write an IUnityContainer decorator, we'd have 39 methods to implement... again... and out of those 39 methods, only 3 or 4 would have any valuable logic...

Interfaces on a diet

What if we slim down those interfaces. Let's pretend for one moment that we have a great job at Microsoft, working for the p&p team; and most of all, we get to design Unity! Woohoo!

Since we've been reading the Coach Factor (or are about to), we decide to slim down those pesky interfaces. What would it look like? Well, we already answered this one:

public interface IUnityContainer

{

    //...

    IUnityContainer RegisterType(Type from, Type to, string name, LifetimeManager lifetimeManager);

}

Oh yeah! We just removed 11 methods out of 39. But now what? Will we put the burden on the client of this interface to always call RegisterType with all 4 parameters? Nah...

 

Extension Methos as Overloads

What if we leveraged our knowledge of extension methods to ease usage of our new slimmed down interface? What would it look like? Exactly! It would exactly look like what we have in the default implementation of the IUnityContainer interface:

public static class UnityContainerExtensions

{

    public static IUnityContainer RegisterType(this IUnityContainer container)

        where TTo : TFrom

    {

        return RegisterType(container, null, null);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, LifetimeManager lifetimeManager)

        where TTo : TFrom

    {

        return RegisterType(container, null, null);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, LifetimeManager lifetimeManager)

    {

        return RegisterType(container, null, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, string name)

        where TTo : TFrom

    {

        return RegisterType(container, null, null);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, string name, LifetimeManager lifetimeManager)

        where TTo : TFrom

    {

        return container.RegisterType(typeof(TFrom), typeof(TTo), name, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, string name, LifetimeManager lifetimeManager)

    {

        return container.RegisterType(typeof(T), null, name, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, Type t, LifetimeManager lifetimeManager)

    {

        return RegisterType(container, t, null, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, Type from, Type to)

    {

        return RegisterType(container, from, to, null);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, Type t, string name, LifetimeManager lifetimeManager)

    {

        return container.RegisterType(t, null, name, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, Type from, Type to, LifetimeManager lifetimeManager)

    {

        return container.RegisterType(from, to, null, lifetimeManager);

    }

 

    public static IUnityContainer RegisterType(

      this IUnityContainer container, Type from, Type to, string name)

    {

        return container.RegisterType(from, to, name, null);

    }

}

Now what?

Well now we can use the full range of overloads and keep IUnityContainer interface as small as possible. It is going to make it a lot easier to mock, decorate and implement the interface without losing any capabilities or ease of use.

Also, we've shared the implementation of overloads throughout all implementations, mocks and decorators of IUnityContainer.

Be careful when designing and trimming down interfaces. It is important that there is no logic associated with the overloads; that is; if registering a component without a name would make a difference and wasn't the equivalent of calling RegisterType(null) then this technique isn't necessarily appropriate.

powered by metaPost

Friday, March 28, 2008 1:56:32 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -
.net | .net - Extension Methods
Search
Archive
<March 2008>
SunMonTueWedThuFriSat
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345
Statistics
Total Posts: 47
This Year: 0
This Month: 0
This Week: 0
Comments: 2
Sign In