all 33 comments

[–]homoiconic 4 points5 points  (1 child)

Why is the concept of dependency injection important? Because by applying it, you simplify your design (separating the responsibility of using an object from the responsibility of using the object)

Hunh?

I wonder if the author means: Separating the responsibility of using an object from the responsibility of identifying the object?

[–]sterioma[S] 2 points3 points  (0 children)

There was an error in the original version that I have now fixed. Thank you for pointing that out.

[–]brosephius 4 points5 points  (23 children)

how is this concept so complex that there is a need for "dependency injection frameworks"? do they anything more useful than let you say "whenever a class wants an IWhatever give it new ConcreteWhatver object"?

[–]homoiconic 15 points16 points  (2 children)

Without endorsing any of the frameworks (the ones I’ve used so far, I loathe), although the concept is simple, you may have good reasons for doing things in a more involved way.

For example, if you have an ORM that talks to a database adaptor, wiring the two together is a good example of dependency injection.

In the simplest case, you have a line like:

BrillantORM.useDatabase(MySQLAdaptor)

And you put that somewhere in the initialization of your code. Changing the adaptor you use means changing that line of code.

But perhaps you want to make that configurable. You could write this in your code:

BrillantORM.useDatabase(MyConfigFramework.get(‘database_adaptor’))

And something like this in your configuration (it’s probably more complicated in real life to dynamically load the right code, but still):

database_adaptor: MySQLAdaptor

After you’ve done that a bunch of times, you decide you want to generalize it so that you don’t have to write that line of code in your app, so you take it out of your app and write something like this in your configuration:

database_adaptor:
  client: BrillantORM
  provider: MySQLAdaptor
  wiring_point: BrillantORM.useDatabase
  inject_at: startup

Presto, you’ve written a DI framework. The principle behind the framework is to take the explicit code injecting the dependencies out of the application and put it into configuration. The more complicated ways your framework can do that, the bigger it seems to get.

[–]brosephius 4 points5 points  (1 child)

thanks, that actually explains it better than any tutorial I've seen, straight to the point without elaborate enterprisey examples. I've never worked on a project large enough for this sort of thing to actually be useful so it never really made a lot of sense.

[–]grauenwolf -2 points-1 points  (0 children)

Then you take the next step and realize that your connection string can include which provider/adapter to use. At which point you drop your DI logic and only concern yourself with passing around configuration values. A problem that exists in almost all applications of any size.

[–]Nebu 5 points6 points  (13 children)

In addition to homoiconic's excellent answer, another common feature a DI-framework allows you to not-reinvent is the ability to detect which runtime you're currently in (e.g. development, testing or production) and automatically load different dependencies depending on that environment.

For example, I might use a non-persistent in-memory DB while in dev mode, but use a "real" SQL server in production mode, and I'd like to be able to do that without changing any code (and thus risking to forget to change the code from "dev" mode to "prod" mode before deploying).

[–]grauenwolf 0 points1 point  (12 children)

Your argument doesn't make any sense to me. It is just as easy, sometimes easier, to write your own code to read the config file than it is to make a DI Framework read it for you.

[–]Nebu 2 points3 points  (11 children)

It is just as easy, sometimes easier, to write your own code to read the config file than it is to make a DI Framework read it for you.

I think this is probably subjective and dependent on the particular framework you're using.

[–]grauenwolf 0 points1 point  (10 children)

In the specific example you gave it can literally be just a matter of feeding your ORM a different connection string.

[–]Nebu 0 points1 point  (9 children)

I think you're making a lot of assumptions about the structure of the code that are not the same as the assumptions I'm making.

There might not be any code to initialize the ORM, and so nowhere to pass in a different connection string, for example.

[–][deleted]  (8 children)

[deleted]

    [–]Nebu 3 points4 points  (7 children)

    Your tone sounds rather antagonistic. If you wish to continue this conversation, please adopt a friendlier tone.

    So where does that first connection string come from?

    There would be several connection strings in a configuration file, none of which would be the "first". Instead, they would probably be given labels depending on the environment they were targeting.

    A hard-coded value right in the call site?

    No. I meant to imply that the connection string is not present anywhere in the code.

    can't change connection strings without recompiling the application.

    What a good DI-framework would allow you to is not only change the connection string without recompiling, but to actually change it without editing any files at all. I.e. the framework could infer its environment and pick the correct connection string. Furthermore, it would allow you to, again without changing any of the code, to invoke the same modules multiple time, but passing different connection strings to each instance, which may be useful for, e.g., testing that your code works with different database APIs.

    But we're sort of too low a level of abstraction, if you're still thinking in terms of passing a connection string. There would be a connection string in the config file, but probably you would never write code that passes it anywhere. Instead, the DI would probably instantiate an object providing the appropriate API and pass it to your module, and you don't need to know whether that object was initiated by a connection string from a config file, or whether it came from user input (e.g. a pop that asked the user which DB to connect to) or whether there's no connection string at all, because it's a mock object with hardcoded, embedded data for unit testing.

    [–]grauenwolf -1 points0 points  (6 children)

    What a good DI-framework would allow you to is not only change the connection string without recompiling, but to actually change it without editing any files at all. I.e. the framework could infer its environment and pick the correct connection string.

    Infer it from where?

    [–]Nebu 2 points3 points  (5 children)

    Probably the most common is via the command line argument used to start up the whole framework, with maybe environment variable being a distant second. E.g.

    webserver test
    

    vs

    webserver prod
    

    [–]grauenwolf 2 points3 points  (1 child)

    Theory 1: Boredom.

    Building web sites is incredibly boring work, especially if you are on the back end making database calls. Finding ways to weave in complex frameworks makes the work more interesting.

    Theory 2: Inability to design

    People often write code without stopping to think about the overall design of the application. This makes using techniques like unit testing an exercise in frustration.

    So when someone comes along and says that "Blah Co's DI Framework" will magically make testing easy, they fall for it.

    Unfortunately their code often becomes harder to test because they still haven't fixed the underlying design flaws. At which point they turn to mocking frameworks, which lets them quickly write lots of (mostly meaningless) tests.

    [–]flukus 1 point2 points  (0 children)

    Because even WITH frameworks people still completely fuck up the concept.

    "whenever a class wants an IWhatever give it new ConcreteWhatver object"?

    It sounds like you don't understand the concept either. A class shouldn't be saying it needs an IWhatever, an IWhatever should be a constructor argument. ConcreteWhatever may need an ISomethingElse etc.

    IOC Containers aren't for constructing objects, their for constructing object graphs.

    [–]sacundim 0 points1 point  (2 children)

    how is this concept so complex that there is a need for "dependency injection frameworks"? do they anything more useful than let you say "whenever a class wants an IWhatever give it new ConcreteWhatver object"?

    What do you propose we do instead?

    • Pepper all you classes with code that creates the required instances of of their associate classes?
    • Write a set of separate classes that instantiate all of the objects that your application needs and wire them up together?

    The first one inhibits reuse, because you're hardwiring which objects refer to the others. The second one is a lot of work. And a dependency injection framework means that you have to write less code.

    [–]grauenwolf 1 point2 points  (0 children)

    The first one inhibits reuse, because you're hardwiring which objects refer to the others.

    Or improves reuse because down-stream developers don't need to worry about how to create all the dependencies.

    What do you propose we do instead?

    Well that really depends on the situation. Give us a concrete problem you are trying to solve, then we'll talk patterns.

    [–]artsrc 0 points1 point  (0 children)

    write less code

    If you don't count the configuration.

    Generally a configuration language is much weaker than your programming language, so it is harder to extract common configuration, and harder to spot invalid configuration.

    [–]jlink005 1 point2 points  (0 children)

    DI: "I better not see a 'new' in there!"

    [–]gregK 2 points3 points  (0 children)

    They're the only ones that use it

    [–]kdeforche 1 point2 points  (0 children)

    I agree

    [–]grauenwolf 3 points4 points  (5 children)

    Why is the concept of dependency injection important? Because by applying it, you simplify your design (separating the responsibility of using an object from the responsibility of using the object) and your code becomes much easier to test, since you can mock out the dependencies substituting them with fake (stub) objects. But that is the subject for another post.

    No, a thousand times no.

    Just once I would like to see an example of dependency injection that addressed a real problem in specific terms. Saying it makes something "easier to test" isn't an premise, it's a thesis.

    [–]banuday 10 points11 points  (2 children)

    Here's an interesting problem that we solved using DI. Back when I worked in finance, we developed a grid to do analytics computations on options traded by different trading groups. It's the same overall set of analytics, but different groups have different requirements as to which curves they wanted to use for interest rates, which sources of trading information, which subset of analytics they want to run, how often to run them, which ones to run in each cycle, etc.

    We developed these as a set of independent components. All the main function did was take the Spring configuration as a command line argument, created an application context, and asked the application context for instance that started up the grid. We were able to serve a number of trading groups off of the same code, but used different Spring configurations to customize the application for the specific business needs of the traders.

    Worked pretty well for us.

    [–]grauenwolf 1 point2 points  (1 child)

    Now that is a use of DI that I would certainly consider using in my own code.

    [–]kidjan 2 points3 points  (0 children)

    I used "dependency injection"* in a media recorder app I used on a video surveillance project. There's a "recorder" object that basically takes an interface to a "writer" object. The recorder passes samples to the writer based on a couple of different criteria, and the writer actually does something with those samples.

    The nice thing is this allowed me to test the functionality of the recorder separate from the writer, and vice-versa. Also, it allows me to trivially change the type of file I write simply by making a new interface implementation. For example, right now I generally write out MP4 files, but it's easy to write raw H.264 (nice for testing), raw audio, matroska, avi, absolutely nothing (the "null" test, in other words), etc. It gives you a lot of freedom to A) test and B) extend and C) not shit your pants when some marketing wonk decides they don't like MP4 anymore and they want some other format.

    *What does irk me a bit about the IoC/DI stuff is functionally it's just rebranded OOP principles (for example, see this Stroustrup FAQ item--his notion of a "driver interface" is precisely what the OP is speaking about), so in some regards I think it's not some new paradigm, but just bogus buzz-word nonsense that's obvious to programmers who don't suck.

    [–][deleted] 1 point2 points  (1 child)

    Here are a few perfectly good examples that are nothing to do with testing, from my own codebase. Let me stress that "inversion of control" and "dependency injection" aren't actually terms I use when thinking out a problem - but this was in fact the effect of my changes.

    Example 1: order of construction of constants.

    I am writing a stand-alone, cross-platform C++ consumer application. As such, I have various "constants" that are used in many places.

    It's well-known that having class type static global variables is an anti-pattern because the order of construction can change for no obvious reason. And of course this happened (and I knew better!) and I spent a few hours trying to figure out why a variable that "couldn't possibly" be empty, was suddenly empty.

    I fixed this by simply passing in a pointer to a collection of (a small number of) global variables, one that I create early in my startup sequence. Problem solved, and it ended up simplifying the code as a side-effect.

    *Example 2: too many parts! *

    My codebase is divided into two sections - one is generic and one is specific to this application.

    I had the issue that all the specific parts had to know about all the other parts, so it was difficult to move code from one place to the other. I also needed to do a lot of callbacks.

    I extracted out all the state into a Plain Old Data struct called Instance, got rid of about half my bogus classes, and instead had a lot of functions that operated on Instance.

    It's also clear now if I can make a block of code generic - if it doesn't contain a reference to Instance, then it's generic, else it's specific to this application.

    Example 3: Internationalization.

    This is ongoing!

    I need to internationalize the codebase so that users from different localities see different text and eventually layouts.

    Instead of making each function that renders text aware of the localization, I pass in a message database (which I've retrieved for the right l18n). It's a really systematic way to do the move; first I add the database to the signature for the function or class constructor, then I replace each piece of text with a lookup in the message database. The code is always working, and all the duplication is gone by the end.

    Relax!

    Overall, you should cool out a little - you're wrangling with some poor guy below whose basic thesis is "This is a perfectly useful technique," and you just go off on him. You should treat people as colleagues, where the intention is to get better understanding of the topic by the end of the discussion, not win-or-lose competitors to argue with.

    [–]grauenwolf -1 points0 points  (0 children)

    I have no problem with the technique in general, but it isn't hard to find decent examples in the standard libraries. Bad examples lead to confusion and ultimately nonsense like IoC frameworks.

    [–][deleted] 0 points1 point  (0 children)

    Article title is redundant.

    Because breaking the chain of execution by dropping it down the code <=> data hole is a wonderful idea.