The other day I came up again with one of those many almost philosophical matters programmers have to tackle so often (sure many come to mind: method vs getter, Constructors vs Create methods, Constructors vs Initialize methods, are Singletons an antipattern?...) this time the reason for my musings was Constructor Injection vs Setter Injection.
To start off, I think we should avoid by all means the chances of creating objects in a non usable state. I mean, you can't guarantee that clients of your classes will follow all the necessary initialization steps for your objects beyond the constructor call itself, so constructors should give you "usable enough" objects, so that they won't crash when methods are invoked on them.
This said, it seems like we should favor construction injection. One problem though, is that we can end up with constructors with huge lists of parameters, which make the code quite difficult to understand. Once your object is no longer magically responsible for creating its dependencies (logger, validator, dao, profiler...) and these have to be provided, they end up in a long parameter list. How to shorten such list is quite a common question. A usual advice is rethinking your design, as so many dependencies could mean that your object is taking over more than one single responsibility. Sure many times this is true, but there are cases where your cohesive object still needs that bunch of parameters. This guy puts it quite well.
Generally I've found if there's more than 3, that's a sign to do a quick sanity check on the design. If there's more than 5, that's a major warning that something is probably wrong with the design.
However, note the word "probably" - in the end, the only real rule is use as many as needed to function, no more and no less. There are always exceptions and cases where more parameters makes the most sense.
A way to reduce the number of parameters is analyzing which of them are related enough to put them into a new class. This is a common refactoring and has the advantage that probably you'll find some logic that can also go into that Transfer Object. Another usual technique is using a Builder with a fluent API. Well, I'm not much fond of this second approach, indeed, in a language featuring named parameters like C#, I would just opt for using them, in the end it's similar to the options objects that we use in jQuery UI (and JavaScript in general), and I think they make the code clear enough.
An approach with regards to Dependendy Injection that I've put to work as of late and that I quite like, is using a mix of constructor and setter injection. I'll use constructor injection for those parameters that are needed for the main functionality of the class, (the repository for your controller, the connectionstring for your DAO...) while for those functionalities that are sort of an extra (like logging), will use Setter Injection. I like the idea of the Constructor signature indicating only those pieces that are essential to the class.
A problem with Setter Injection is, what if someone decides to skip the IoC and create the object manually, and forgets to set the property? (certainly, you can't forget to invoke a constructor, but can forget about the setter). Well, to be on the safe side we'll need to initialize our setter dependencies with some "usable default", for example using the Null Object Pattern.
One more point for me is that when going through the container I'd like the system to work if these "non essential components" have not been registered (we forget to register a logger for example). With this in mind, we should declare such dependencies as optional. In Unity, this means using the [OptionalDependency] attribute.
When putting together these 2 techniques I've just mentioned, there's something more to take into account. Unity will invoke the constructor and then will do the Setter Injections. For those Optional Dependencies that have not been registered, it'll set it to null. So, to avoid overwriting that safe Null Object that we have previously set, we'll need a check in our setter. All in all:
public class WebSocketsInterfaceRunner { private ILogger logger = new NullLogger(); [OptionalDependency] public ILogger Logger { get { return this.logger; } set { //if defined as optional, the container will set it to null if it finds that it's not defined, //so we need this null check if (value != null) this.logger = value; } }
This post shows a mindset rather similar to mine pertaining Dependency Injection.
No comments:
Post a Comment