interface IDependancy { } class DefaultDependancy : IDependancy { }
... I have seen many Dependency Inversion (DI) patterns implemented in the follow way...
class Worker { private IDependancy _dependancy; Worker() : this (null) { } Worker(IDependancy dependancy) { if (dependancy == null) _dependancy = new DefaultDependancy(); else _dependancy = dependancy; } }
I'm also guessing that you've probably gotten this far, thought "seems about right" and are about to flip back to see if anything new's happened on Twitter? Well, hold on - 'cos you're wrong!
The problem with the above scenario is that it doesn't correctly convey the intention of the pattern. The content of the non-default constructor is being asked to cope with two, entirely different, situations.
- "Did the client intentionally, and knowingly, call the default constructor" or
- "Did the client call the non-default constructor directly and accidentally pass in a null reference?"
This is a perfect recipe for the most wonderful 'managed' error: NullReferenceException. This will be made even more troublesome if you haven't yet switched to using small methods as you'll have no idea what's null. Enjoy!
So, here's what you should be doing ...
class Worker { private IDependancy _dependancy; Worker() : this(new DefaultDependancy()) { } Worker(IDependancy dependancy) { if (dependancy == null) throw new ArgumentNullException("dependancy"); _dependancy = dependancy; } }
Now we know that if the non-default constructor's sole parameter is null then it's because there's a problem. Actively throwing our own ArgumentNullException also allows us to take control of the situation and name the offending variable.<
This simple change removes any ambiguity in your code and makes your intentions abundantly clear.
No comments:
Post a Comment