Implicitly Delicious

One great new feature in Strange v0.7 (release candidate now on dev) is implicit bindings. This work comes to us courtesy of Strange super-contributor Will Corwin, author of Strange Signals. Implicit bindings adds a groovy mechanism for cleaning out the ‘routine’ mappings that sometimes clutter your Context.

As usual, let’s start with an illustrative example. Let’s say you’ve created an AvatarService for your game to handle the saving and retrieving of avatar data. Because you’re a diligent programmer, you also have an IAvatarService interface. Here’s the mapping as it would appear in your Context:

injectionBinder.Bind<IAvatarService>().To<AvatarService>().ToSingleton();

Awesome. You’re doing the good IoC thing: injecting and writing to the Interface rather than the concrete class. But, er, come over here where nobody can hear us. Now, between you, me and my ravenous DeathDroid, the truth is that you only have one AvatarService, right? Or you have one that you use 90 percent of the time? In fact, you probably have many classes like this, where the mappings are the same each and every time you build a new Context. These are routine mappings — bindings that look the same nearly every time you map them. Wouldn’t it be nice to have all the advantages of binding without cluttering up your Context with all this pro forma boilerplate? Don’t we all just want to walk into the Strange Bar and bark out “Gimme the usual?”

This is exactly what implicit bindings are for. By allowing you to specify default implementations, we can now reserve the Context only for mappings that are different from “yer usual.” For now, Implicit bindings allow you to do the following things:

  • Tag an interface with the class that implements it by default
  • Tag a class with one or more interface(s) it implements by default
  • Tag a View with the default Mediator
  • Tag a Mediator with a default View

Implicit Injection Bindings

The first two bullet points above describe injection bindings. For example, you could achieve our AvatarService binding by doing either of the following:

// Tagging an implementor
namespace com.example
{
    [Implements(typeof(IAvatarService))]
    public class AvatarService : IAvatarService
    {
        //body of the class
    }
}

// Tagging an interface
namespace com.example
{
    [ImplementedBy(typeof(AvatarService))]
    public interface IAvatarService
    {
        //body of the interface
    }
}

While you can tag either Interface or Implementor, as a best practice we recommend tagging the Implementor. Tagging the Interface probably fits a special case for library distribution, but is not ideal for standard use. Note that there is a priority order to implicit injection binding, as follows:

  • Interfaces give way to
  • Implementors, which give way to
  • Explicit bindings in the Context.

Also note that you can create Singleton, named, single-Context and cross-Context bindings implicitly. You cannot yet create factory or value bindings (maybe in a future release).

Implicit Mediation Binding

We apply the same idea to Mediation.

// tagging a View
namespace com.example
{
    [MediatedBy(typeof(ShipMediator))]
    public class ShipView : View
    {
        //body of the class
    }
}

// tagging a Mediator
namespace com.example
{
    [Mediates(typeof(ShipView))]
    public class ShipMediator : Mediator
    {
        [Inject]
        public ShipView view { get; set; }
        //body of the class
    }
}

As with injections, there’s an order of priority for Mediation bindings:

  • Views give way to
  • Mediators, which give way to
  • Explicit bindings in the Context.

We consider it best practice to tag the Mediator, not the View.

Magic: The Binding

Now, there is one more step you need to do to get all this automagic binding to work. Like Signals, Will’s made implicit bindings an opt-in feature, so you need to tell Strange (1) that you want to use it and (2) which namespaces you want scanned for binding. Yes, that’s right, implicit binding requires namespaces. The required code is simple (we suggest putting it at the top of your mapBindings method).

override protected void mapBindings()
{
    string[] namespaces = new string[]
    {
        "somenamespace.i.want.scanned",
        "another.namespace"
    };
    implicitBinder.ScanForAnnotatedClasses(namespaces);
    // add other bindings here
}

Scans include child namespaces, so if you scan “com.example”, the scan covers “com.example.myproject” and “com.example.myproject.services”, etc.

And there you go! Implicit bindings that go down smoother than a sherry-finished Balvenie.

Advertisements