Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
    
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. –  Lumirris Apr 15 '13 at 19:47
add comment

1 Answer

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.

share|improve this answer
    
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? –  Lumirris Apr 17 '13 at 0:00
    
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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