Hacker News new | ask | show | jobs
by shadowmint 4733 days ago
sigh

DI is not the same as an IOC container. Get it right ffs, if you want people to take you seriously.

DI:

    class Blah():
      def __init__(self, dependency):
        self.dep = depedency
IOC:

    my_blah = container.resolve(Blah)

Anyway, onto my main complaint:

    "Pinject is a pythonic dependency injection library. Python ports of other libraries, like 
     Spring or Guice, retain the feel (and verbosity) of being designed for a statically typed
     language. Pinject was designed from the ground up for python."
Wow, I'm impressed. Designed from the ground up for python. That's why you needed ~20 pages of documentation on how to use it?

Intuitive.

Also, what's wrong with annotations? I'm rather fond of this sort of code:

    ### implements, resolve, Scope
    class IType(object):
      prop1 = None
      def call(self):
        pass

    @implements(IType)
    class ImplType(object):
      prop1 = "Value"
      def call(self):
        pass

    scope = Scope()
    scope.register(ImplType)

    @resolve(scope)
    class UsesType(object):
      def __init__(self, t=IType):
        self.t = t

    instance = UsesType()
Or in pinject:

    obj_graph = pinject.new_object_graph(binding_specs=[SomeBindingSpec()])
    needs_provider = obj_graph.provide(NeedsProvider)
But wait, I want a test. Ok~

    instance = UsesType(my_mock)
Or in pinject:

    ?????? 
In fact, pinject doesn't even mention tests in its documentation. Whew~ That's only the whole reason for using DI in the first place.

Self important library is self important. Not amazed.

2 comments

"IOC: my_blah = container.resolve(Blah)"

That's not IOC. That's service location. Which is only used at the "composition root" (look it up) in IOC.

"In fact, pinject doesn't even mention tests in its documentation. Whew~ That's only the whole reason for using DI in the first place."

No, testing accounts for just one of the more minor reasons to be using DI.

I would argue the single greatest benefit (possibly, only benefit) that DI provides over other service location options (eg. factories) is that you get a nice means to test independent units of code by providing mock implementations.

In dynamic languages with no real 'private' members, the benefit is actually pretty minimal because you can assign mock service instances after the object is created.

eg.

    test_instance = Blah()
    test_instance.service = MockService()
vs.

    test_instance = Blah(MockService())
...which means, there isn't a lot of point in using it, unless you're using an IOC container which gives you some other synergy bonuses like singletons, per thread and per request instances, configuration file driven implementation binding, etc.
No the single greatest benefit that a IOC container provides is object lifetime and scoping management. The next greatest benefit is the ability to decompose your code base into a single responsibility adhering components with good decoupling and namespace cohesion characteristics.

Testing is very low down on the list of advantages of a IOC container. And even then, most people that think they're doing testing properly with a IOC container are doing it wrong. Hint: "Re-binding" your container by forcing it to drop an existing service implementation and replacing it with your mock/test implementation is "doing it wrong".

The reason there is always so much discourse around DI and IOC is because very few programmers actually bothered to learn, and grok it, even if they think they already have.

> Testing is very low down on the list of advantages of a IOC container. And even then, most people that think they're doing testing properly with a IOC container are doing it wrong. Hint: "Re-binding" your container by forcing it to drop an existing service implementation and replacing it with your mock/test implementation is "doing it wrong".

What part of that is wrong? I may be missing your focus, but that sounds right for unit testing.

It's wrong because your unit tests, just like your application, should not be aware of the presence of an IoC container except at the absolute composition root.

Your unit test libraries should have their own composition roots of their own and it is at this level, in configuring the IoC "modules" (as they are often called) that special mock services would be provided.

If your test cases are littered with crap concerning your IoC container, re-binding stuff, then something is badly wrong. Sadly, pretty much all projects I've seen that use IoC end up writing their tests in this way. Someone wasn't thinking.

Your tests should be treated with the same adherence to SOLID principles as your application code. They are first class citizens of your code base. So why dirty your tests by having them coupled to your IoC container?

I agree. Testing with heavily mocked implementations is basically useless. Especially under time pressure, I tend to focus more on system and integration tests than on cute unit tests that tend to find issues that never occur in practice and don't find issues that occur when the first user has his first minutes with the system.

"Hooray, my setName sets the name correctly!" - well, my congratulations.

The direct opposite of Dependency Injection is the Service Locator pattern.

    my_blah = container.resolve(Blah)
Notice how there is no injection of dependencies at all.

I think you aught to learn what DI actually is... when you say that "the single greatest benefit (possibly, only benefit) that DI provides over other service location options" you are saying that DI is a form of service location (sic), which isn't the case.

> The direct opposite of Dependency Injection is the Service Locator pattern.

You're right in spirit, but the direct opposite of dependency injection is obviously direct dependency construction, not service location.

Service location is one step up from direct dependency construction, and can be enough for a lot of cases, but as you say, it's not dependency injection.

You're right in spirit, but the direct opposite of dependency injection is obviously direct dependency construction, not service location.

Fair call!

another thing - there are multiple ways to inject dependencies - not just constructor injection.
:)

hint: the DI is in the constructor to Blah when the container returns an instance. (all IOC frameworks have a top level resolve call somewhere).

Yeah, to startup the container and manage the object graph, sure. But not to directly instantiate the objects like you've given in your example!

The whole point behind DI is to decouple objects, any IoC container worth it's salt gets started and works it out for you. You shouldn't need to resolve the individual object.

Another poster noted this is known as composition root, a good explanation can be found here:

http://blog.ploeh.dk/2011/07/28/CompositionRoot/

Note the following:

"A DI Container should only be referenced from the Composition Root. All other modules should have no reference to the container."

Words to live by :-)

True indeed, however, the point remains:

IOC is resolving an instance with it's dependencies.

How you do that is irrelevant.

The point is that the action of resolving dependencies is NOT dependency injection.

If you're writing an IOC container, call it an IOC container.

aught = something. ought = should.
I never realized! Thanks, I'll remember that.
Less documentation is better? That's not something I hear every day.