Skip to content

Instantly share code, notes, and snippets.

@mxriverlynn
Created August 20, 2012 21:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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?

@tbranyen
Copy link

Parameterized constructors that can reject instance creation from incorrect types is probably the biggest thing I can think of.

@jbogard
Copy link

jbogard commented Aug 20, 2012

Third way:

var c = new C();

That seems the easiest and least amount of burden on the caller.

@jbogard
Copy link

jbogard commented Aug 20, 2012

And if I wanted to test this (since I would need to know the intimate details of how C uses A and B):

c.a.foo = function() { return "I'm in ur codez"; };

@bennage
Copy link

bennage commented Aug 20, 2012

I think we're presenting this question with a slight bias. I'd really love to have people examine the code in question and make concrete arguments about what they don't like and why.

The context of this discussion is here:
http://hilojs.codeplex.com/discussions/391074

I wrote this JavaScript code with this test. I was responding to this earlier code. However, Derick and I both agree that the earlier code is not desirable.

Perhaps a better example to compare would be this code and this test.

@bennage
Copy link

bennage commented Aug 20, 2012

The significant different (in my mind) is that the object under test exposes properties that we want to replace at test time.

var A = function() { /* setup stuff */ };
A.prototype.someFunction = B.someFunction;
A.prototype.methodToTest = function(input) {
var result ={};
result.stuff_I_dont_care_about = this.someFunction();
result.stuff_I_want_to_test = input.; // calculate some stuff with input
return result;
};

@bennage
Copy link

bennage commented Aug 20, 2012

Then in my test:

var sut = new A();
sut.someFunction  = function() { /* don't care */ };
var result = sut.methodToTest();
// assert some stuff 

@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