Saturday, May 3, 2014

Public API's - Using Dependency Injection (while avoiding user hate)

As many developers have discovered, Dependency Injection (DI) is a very useful pattern.  It allows a developer to see what dependencies a class has, or in simpler terms, it shows what other objects the object being constructed interacts with.  DI goes hand in hand with Inversion of Control (IoC), which allows us to set up a IoC container that will inject the correct dependency when needed.  This is all typically wired up at one place in an application called the application root.

One of the biggest reasons to use DI is testability.  It allows mocks to be injected into the classes constructor when testing.

What does all this have to do with building a public API?  Well, when your users go to use the library that you crafted with this best practice idea of DI, they may be in for a nasty surprise.   Some developers that don't want to be bothered with DI but want to use your framework.  The object graph wire-up may look something like this...

public class TheAPIConsumersClass
{
    Airport WireUpTheShinyNewAirportAPI()
    {
       var fuel = new Fuel();
       var engine = new Engine(fuel);
       var prop = new Prop();
       var airplane = new AirPlane(engine,prop);
       var controlTower = new ControlTower();
       var airport = new Airport(airplane, controlTower);
       return airport;
    }
}

When all your user really wants to do is this something like this...

public class TheAPIConsumersClass
{
    Airport WireUpTheShinyNewAirportAPI()
    {
       var airport = new Airport();
    }
}

I found myself in this situation with our API design recently.  I didn't want to lose the DI that I'd been using for testing, yet I didn't want to expose all the constructors to the users of the API.  After searching around the Internet, I came up with something I could live with.  

Mark the constructor with dependency injection as internal.  Have a public constructor which calls the internal constructor, provides the required dependencies.  


public class MyApi
{
   public MyApi() : this(new Thing1(), new Thing2())
    {  
    }
  internal MyApi(Thing1 thing1, Thing2 thing2)
    {
          _thing1 = thing1;
          _thing2 = thing2;
    }
}



More food for thought...

No comments:

Post a Comment