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:
- if a category of binding cannot be resolved from the
IBuilderService.Create
category
argument, then aNullObjectBuilder
should be resolved - this is the default binding in this case. - if a clientID of a binding cannot be resolved, then a default client implementation like
CatOneBuilder_DefaultClient
for acategory
ofCatOne
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– tgkprog2013年04月15日 19:27:33 +00:00Commented Apr 15, 2013 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– Jeff2013年04月15日 19:47:57 +00:00Commented Apr 15, 2013 at 19:47
1 Answer 1
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– Jeff2013年04月17日 00:00:45 +00:00Commented Apr 17, 2013 at 0:00
-
1Some 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– Stephen C2013年04月19日 09:41:23 +00:00Commented Apr 19, 2013 at 9:41
Explore related questions
See similar questions with these tags.