3

I'm using Ninject, but this is not a Ninject-specific question. I'm wondering if the advanced and flexible capabilities of the IoC container are giving me enough rope to hang myself with a bad application design.

I've got a service interface:

public interface IBuilderService {
    IBuilder Create(string category, string clientID);
}

and I lean on Ninject Factory Extension's ToFactory() to create what I'd guess you'd call a proxy implementation at runtime.

I have IBuilder bindings like this:

kernel.Bind<IBuilder>().To<NullObjectBuilder>(); // for unbound categories
kernel.Bind<IBuilder>().To<CatOneBuilder>();
kernel.Bind<IBuilder>().To<CatTwoBuilder>();
kernel.Bind<IBuilder>().To<CatThreeBuilder>();
kernel.Bind<IBuilder>().To<CatOneBuilder_ClientA>();
kernel.Bind<IBuilder>().To<CatOneBuilder_DefaultClient>(); // for unbound clients
// etc...

I've been experimenting with custom instance providers and binding generators with Ninject (I'm guessing other IoC containers may have similar constructs for overriding basic binding behavior), but I have two distinct binding scenario goals:

  1. if a category of binding cannot be resolved from the IBuilderService.Create category argument, then a NullObjectBuilder should be resolved - this is the default binding in this case.
  2. if a clientID of a binding cannot be resolved, then a default client implementation like CatOneBuilder_DefaultClient for a category of CatOne should be resolved - this is the default binding in this case.

This is a situation where unknown category requests are expected, so it seems a shame to use expensive exception handling for something I know will happen very often. I want there to be fallback behavior to "handle the unhandled" in a very plain way - in this case, it would be the NullObjectBulder for unknown categories, and *_DefaultClient for unknown clients of known categories - two distinct fallback behaviors for two different types of unknown behavior.

I'm having trouble keeping the two fallback behaviors mapped to their respective scenarios, but it's given me pause to think about the friction I'm feeling in fleshing out this design…

Is it a flawed design to have default bindings and the logic used to determine when one is needed contained in IoC custom implementations, rather than an exception being thrown? Is this a mis-use of IoC containers?

  • I think its quite nice to not get a null. do not have to sprinkle != null all over (at least for this) – tgkprog Apr 15 '13 at 19:27
  • @tgkprog I agree, but where does this protection from null belong? And in the one case, it's a simple null object pattern employed, but in the second case, it's actual functioning default implementation that's needed. That's satisfying a greater requirement than simply not being null. – Jeff Apr 15 '13 at 19:47
3

It is not misuse ... but it isn't necessarily good design either.

It really depends on the context.

  • If the purpose is to allow you to build simple configurations in a less verbose way, and the defaults are such that you achieve that goal ... then that would suggest that this is a good design.

  • If the purpose is to allow you be careless without getting exceptions and/or the defaulting is likely to result in configurations that are likely to be incorrect (versus what you meant) ... that would suggest a poor design.

Use (or not) of defaulting is a trade-off between the "goodness" of making things explicit, and the "badness" of being verbose. You really need to make up your own mind what is going to work best for you in the context of your problem.

Or another way to look at it is to say that you should be selective in what "injection points" you define defaults for.

  • Good points. In this case, the only exception I want to avoid is from the IoC container when it can't resolve an appropriate binding for a given type. I think I can reasonably protect against trying to bind a default type to an interface it doesn't implement. So, I find myself looking at creating my own factory to create instances, with a default instance if no better candidate can be chosen. But since the instances have their own dependencies, I want to use an IoC which means the factory needs a reference to the IoC instance (barf!). This can't be an esoteric use case, can it? – Jeff Apr 17 '13 at 0:00
  • 1
    Some IoC frameworks (e.g. Spring) have a facility for notifying interested objects that the injection has completed. You can use that notification to have objects validate themselves. This is very useful when you can't validate at the instant of injection. – Stephen C Apr 19 '13 at 9:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.