Skip to content

Instantly share code, notes, and snippets.

@mxriverlynn
Created August 20, 2012 21:43
Show Gist options
  • Save mxriverlynn/3408167 to your computer and use it in GitHub Desktop.
Save mxriverlynn/3408167 to your computer and use it in GitHub Desktop.
constructor dependencies vs setters

I'm re-evaluating my thoughts on constructor based dependencies vs setter dependencies in JavaScript. I'm trying to see if the same reasons we avoid setter injection in static language like C# and Java still apply in JavaScript.

In other words, why is this:

var a = new A();
var b = new B();

var c = new C(a, b);

better than this:

var a = new A();
var b = new B();

var c = new C();
c.a = a;
c.b = b;

What are the reasons that you would choose constructor parameters vs setters, and why?

What other options are there for runtime composition of the c object, so that it can correctly make use of a and b? When would you choose those options?

@mbriggs
Copy link

mbriggs commented Aug 20, 2012

What are the reasons that you would choose constructor parameters vs setters, and why?

  1. constructor params make it obvious what the creation contract is for the class. in js, this is arguably less of a big deal, since half our classes accept an options object so we can fake named params, but at the end of the day being able to look in a single place to find dependencies is a Good Thing
  2. your contstructor function often will do some "destructuring" work. So if my class takes a Person, and I want firstName and lastName off of it, keeping that in the constructor function helps make it more obvious what the implicit interface (or protocol) is between the dependencies you are passing in, and the class. It also means the rest of the class doesn't need to change if you change it to get firstName and lastName from somewhere else.

What other options are there for runtime composition of the c object, so that it can correctly make use of a and b? When would you choose those options?

You can use a service locator pattern. Something like

function C(){
  this.a = FooLocator.a();
  this.b = FooLocator.b();
}

I would use this if the thing I am instantiating is variable based on environment -- like say a client side i18n translator object where you want a different one based on locale, and a passthrough in a dev environment.

You can also use an IoC container pattern (like angularjs). Some people thing the only reason is for composition during test time, and tests can just monkey patch in js, so why have IoC. I think that the real benefit is that you are giving the wiring up of a complex dependancy graph its own abstraction, and if you don't have this you will basically end up doing it anyways in something called "app.js" or "init.js" anyways. Using a full fledged container means that you are giving that code structure, and not going to have to duplicate various bits when you start running tests. For simple cases it isn't worth it, but for complex cases it is.

@mxriverlynn
Copy link
Author

@mbriggs - good info. i especially like the bit about destructuring. that could still be done with a setter method (as opposed to just assigning an attribute), but I agree that the encapsulation feels better to me when it's in the constructor.

fwiw, though, I see IoC containers in JS as an anti-pattern. It's as if we've become so used to the necessity of them in languages like C# and Java that we forgot what problems they introduced when looking at what problems they solved. I've never found a legitimate need for them in JS. The times when I thought that it would be necessary or helpful were the times that I was ignoring the pain of the design I was implementing: object graph too deep, tight coupling between processes that should be separated, etc. Typically flattening the object graph by recognizing additional boundaries within our objects will make the need for an IoC container go away, IME.

@jbogard is right about having more options available in JavaScript and needing to explore and understand those options, when they're useful, when they're detrimental, etc (via our twitter conversation). and even though i react against his suggestion here, i end up doing that on a regular basis - mocking $.ajax calls, for example.

I still don't like setter injection, especially when it takes the form of foo.bar = baz. If I were going to do setter injection, I would create a setter method. This at least gives a legitimate API for the purpose of setting the dependency, which has more value in declaring the intention of the code than simple attribute assignment.

... so much to think about. so little time :P

@Encosia
Copy link

Encosia commented Aug 23, 2012

I'm not sure I understand the context 100% here due to the abstract object names. The relation between A, B, and C might make a difference in how I viewed the situation (e.g. if A and B only existed to hang off of Cs, then my opinion might change, but I'm not sure).

That said, I would almost always prefer to see something like this in code that I need to work with, assuming A and B are available at the time that you construct C:

var a = new A();
var b = new B();

var c = new C({
  A: a,
  B: b
});

Or, just:

var c = new C({
  A: new A(),
  B: new B()
});

To me, it feels like a more idiomatic approach that matches what I'm accustomed to from working with client-side libraries. I know it's essentially the original first choice rehashed, but seeing it that way makes the decision more clear to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment