Some advice for wire.js: when should I use property injection and when should I use arguments?

239 views
Skip to first unread message

Andrew Eisenberg

unread,
Sep 25, 2013, 12:59:46 PM9/25/13
to cuj...@googlegroups.com
Looking for some philosophical reasons to inject properties as described here: https://github.com/cujojs/wire/blob/master/docs/configure.md#properties vs using init/ready methods as described here:https://github.com/cujojs/wire/blob/master/docs/configure.md#init-methods

In my particular case, I have a bunch of backbone views all getting configured in a single module.  These views all require a dom node.  I am currently using an init method of the module to initialize the views, and I am using DI to add the required dom nodes as properties.

So, my viewer module def looks like this:

    viewer: {
        create: {
            module: 'viewer',
            args: [
                { $ref: 'view1' },
                { $ref: 'view2 },
                ...
            ]
        },
        properties: {
            view1DomElt: { $ref: 'dom!view1Elt'},
            view2DomElt: { $ref: 'dom!view2Elt'},
            ...
        },
        init: 'init'
    },

This all works, but I am wondering whether the dom nodes should be properties and the views should be args, or all should be properties, or all should be args?

unscriptable

unread,
Sep 25, 2013, 2:21:47 PM9/25/13
to cuj...@googlegroups.com
Hey Andrew!

On whether to inject properties via the properties facet or to inject things via init: I think it' s just a matter of preference.  The reason both exist is to be more compatible with what's already been built.  Since there are plenty of third-party and first-party things that use properties or init methods, we've provided both.

In regards to injecting via ready, I'm having a hard time coming up with use cases, but it's possible that somebody would want to ensure that certain dependencies are provided very late in the creation cycle, I guess. :)

Your example uses a constructor, which is yet another case.  Devs might require that dependencies be injected via constructor if the lack of those dependencies might cause API issues.  For instance, let's say you've got a Collection constructor whose prototype has an add(item) method and a addComparator(func) method.  This API could cause unnecessary internal re-sorting of the items in the collection if the dev doesn't call addComparator() before calling add().  If a comparator function were a required constructor param, though, there would be no thrashing -- and the code inside Collection's methods would likely be simpler.  

However, in wire's object literal syntax/DSL, APIs that use property injection or init/start injection are a bit better since you can create dependency cycles that wire can resolve.  wire can't resolve dependency cycles when the components are using constructor injection.  (Brian: is this accurate?)

I guess I'd sum up my reason for using constructor injection is that it helps me write a saner implementation and a saner API under certain circumstances.  If I can still write something sane while using property injection, that's what I do, though.

-- John

unscriptable

unread,
Sep 25, 2013, 2:27:06 PM9/25/13
to cuj...@googlegroups.com
Seems like this should go in a tutorial. :)

Brian Cavalier

unread,
Sep 25, 2013, 3:10:48 PM9/25/13
to cuj...@googlegroups.com
Hey Andrew, great questions.

Yeah, what John said :)  It really comes down to what the particular component is expecting in terms of its API and initialization requirements.  Obviously, if you're dealing with a component that needs a DOM Node in its constructor in order to initialize properly, then you just have to go with a constructor arg.

tl;dr  I tend to prefer properties when I have the luxury of designing the components.

There are a few other things to keep in mind, though:

First, wire "releases" a component (allows it to be dereferenced, e.g. via $ref) at a very specific time in its lifecycle: after the "initialization" lifecycle step, which is the step where the "init" facet will be executed.  That means that a component's creation, property setting, and init method(s) will run *before* any other components can get their hands on it.  This is intended to give you enough "room" to ensure that a component can't be poked too early accidentally.  Connections (via on, connect, aop, etc.) and the "ready" facet all run after that.

So, while the choice of constructor args vs. properties doesn't have a bearing on much in terms of collaborating components, it's important to note the difference between "init" and "ready": init is guaranteed to happen before a component is allowed to be dereferenced, whereas "ready" happens as late as possible, and perhaps importantly, after connection type facets are applied.

Another important thing, as John mentioned, is dependency cycles.  It's technically impossible to resolve a dependency cycle involving constructors, whereas dependency cycles between properties is possible, in many cases, to resolve.  So, typically its better to go with something later, like properties, if you can.

I tend not to use "init" for setting dependencies, mostly because it means I have to go and write an init method that, usually, just ends up setting a bunch of properties ... which I could do with wire directly anyway.  There are times, though where having an init method makes sense.  For example, sometimes a good pattern is to set a bunch of properties, then poke the component via "init" so that it can do some necessary programmatic initialization work using those already-set properties.  Since init is guaranteed to happen after properties, but before the component can be dereferenced, that can work out nicely.

Also, "create" can do more than call a constructor or function.  If you pass it an *object* it will run Object.create on it.  This can be a really neat way to create new instances.  For example, instead of writing a module that is a zero arg constructor plus a prototype, you can just write an object, use "create" to beget a new instance, then use properties to configure it further.  The cujoJS TodoMVC controller is done this way (https://github.com/cujojs/todomvc/blob/cujo/labs/architecture-examples/cujo/app/controller.js and https://github.com/cujojs/todomvc/blob/cujo/labs/architecture-examples/cujo/app/main.js#L102).  It's not appropriate for all situations, but can be a very clean alternative.

A more superficial aspect of this is that I tend to find using properties to look a little nicer in a wire spec.  More than one constructor tends to nest more deeply and be uglier to my eyes.

Scott Andrews

unread,
Sep 26, 2013, 10:08:05 AM9/26/13
to cuj...@googlegroups.com
Chris Beams had a really good talk on styles of dependency injection. It would be interesting to take that content and rehash it for wire.js


-Scott

Sent from Mailbox for iPad


--
You received this message because you are subscribed to the Google Groups "cujojs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cujojs+un...@googlegroups.com.
To post to this group, send email to cuj...@googlegroups.com.
Visit this group at http://groups.google.com/group/cujojs.
To view this discussion on the web visit https://groups.google.com/d/msgid/cujojs/a5aeb085-0862-401f-9d4a-5f11a460537a%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

unscriptable

unread,
Sep 26, 2013, 5:14:03 PM9/26/13
to cuj...@googlegroups.com
Yah, that's a great talk.  Got some time this week or next to wire-ify it?  :)  -- J

Brian Cavalier

unread,
Sep 27, 2013, 9:57:32 AM9/27/13
to cuj...@googlegroups.com
I really like that talk.  Simple and right to the point, and I like that Chris says to pick the best approach for the task at hand.  That def aligns with our direction for the wire.next prototyping: simple core plus extensible ways to specify a composition plan.


On Thursday, September 26, 2013 10:08:05 AM UTC-4, scothis wrote:
Reply all
Reply to author
Forward
0 new messages