104

I am having a hard time looking for resources on why I should use dependency injection. Most of the resources that I see explains that it just passes an instance of an object to another instance of an object, but why? Is this just for cleaner architecture/code or does this affect performance as a whole?

Why should I do the following?

class Profile {
    public function deactivateProfile(Setting $setting)
    {
        $setting->isActive = false;
    }
}

Instead of the following?

class Profile {
    public function deactivateProfile()
    {
        $setting = new Setting();
        $setting->isActive = false;
    }
}
20
  • 8
    You are introducing a hard-coded dependency to deactivateProfile() (which is bad). You have more decoupled code in the first one, which makes it easier to change and to test. Commented Nov 13, 2018 at 8:18
  • 3
    Why would you do the first one? You're passing in a Setting and then ignoring its value. Commented Nov 13, 2018 at 8:26
  • 62
    I don't agree with the downvotes. While the subject matter may be considered trivial to experts, the question has merit: if dependency inversion should be used, then there should be a justification for using it.
    – Flater
    Commented Nov 13, 2018 at 9:58
  • 13
    @PhilNDeBlanc: This code is clearly oversimplified and not really indicative of real world logic. However, deactivateProfile suggests to me that setting the isActive to false without caring about its previous state is the correct approach here. Calling the method inherently means that you mean to set it as inactive, not get its current (in)active status.
    – Flater
    Commented Nov 13, 2018 at 10:13
  • 2
    Your code is not an example of dependency injection or inversion. It's an example of parameterization (which is often much better than DI).
    – jpmc26
    Commented Nov 13, 2018 at 16:06

9 Answers 9

108

The advantage is that without dependency injection, your Profile class

  • needs to know how to create a Settings object (violates Single Responsibility Principle)
  • Always creates its Settings object the same way (creates a tight coupling between the two)

But with dependency injection

  • The logic for creating Settings objects is somewhere else
  • It's easy to use different kinds of Settings objects

This may seem (or even be) irrelevant in this particular case, but imagine if we're not talking about a Settings object, but a DataStore object, which might have different implementations, one that stores data in files and another that stores it in a database. And for automated tests you want a mock implementation as well. Now you really don't want the Profile class to hardcode which one it uses - and even more importantly, you really, really don't want the Profile class to know about filesystem paths, DB connections and passwords, so the creation of DataStore objects has to happen somewhere else.

9
  • 23
    This may seem (or even be) irrelevant in this particular case I think it is very much relevant, in fact. How would you get the settings? A lot of systems I've seen will have a hard-coded default set of settings and a public facing configuration, so you'd need to load both and overwrite some values with the public settings. You may even need multiple sources of defaults. Perhaps you might even be getting some from disk, others from DB. So, the entire logic for even getting settings can, and often is, non-trivial - definitely not something consuming code should or would care about.
    – VLAZ
    Commented Nov 13, 2018 at 14:55
  • We could also mention that object initialisation for a non-trivial component, such as a web service, would make $setting = new Setting(); horrendously inefficient. Injection and object instantiation happens once. Commented Nov 14, 2018 at 8:55
  • 9
    I think using mocks for testing should have more emphasis. Chances are if you look just at the code, it's always going to be a Settings object and will never change, so passing it in seems like a wasted effort. However, the very first time you try to test a Profile object by itself without also needing a Settings object (using a mock object instead as a solution) the need is very apparent.
    – JPhi1618
    Commented Nov 14, 2018 at 16:49
  • 2
    @JPhi1618 I think the problem with emphasising "DI is for Unit Testing" is that it just leads to the question "why do I need Unit Tests". The answer may seem obvious to you, and the benefits are definitely there, but to someone just starting out, saying "you need to do this complicated-sounding thing in order to do this other complicated-sounding thing" tends to be a bit of a turn-off. So it's good to mention different advantages which might be more applicable to what they're doing right now.
    – IMSoP
    Commented Nov 14, 2018 at 18:09
  • 1
    @user986730 - that's a very good point,and highlights that the real principle here is Dependency Inversion, of which Injection is just one technique.So for example, in C, you can't really inject dependencies using an IOC library, but you can invert them at compile time, by including different source files for your mocks, etc, which has the same effect. Commented Nov 15, 2018 at 11:28
64

Dependency Injection makes your code easier to test.

I learned this first-hand when I was tasked with fixing a hard-to-catch bug in Magento's PayPal integration.

An issue would arise when PayPal was telling Magento about a failed payment: Magento wouldn't register the failure properly.

Testing a potential fix "manually" would be very tedious: you'd need to somehow trigger a "Failed" PayPal notification. You'd have to submit an e-check, cancel it, and wait for it to error out. That means 3+ days to test a one-character code change!

Luckily, it appears that the Magento core devs who developed this function had testing in mind, and used a dependency injection pattern to make it trivial. This allows us to verify our work with a simple test case like this one:

<?php
// This is the dependency we will inject to facilitate our testing
class MockHttpClient extends Varien_Http_Adapter_Curl {
    function read() {
        // Make Magento think that PayPal said "VERIFIED", no matter what they actually said...
        return "HTTP/1.1 200 OK\n\nVERIFIED";
    }
}

// Here, we trick Magento into thinking PayPal actually sent something back.
// Magento will try to verify it against PayPal's API though, and since it's fake data, it'll always fail.
$ipnPayload = array (
  'invoice'        => '100058137',         // Order ID to test against
  'txn_id'         => '04S87540L2309371A', // Test PayPal transaction ID
  'payment_status' => 'Failed'             // New payment status that Magento should ingest
);

// This is what Magento's controller calls during a normal IPN request.
// Instead of letting Magento talk to PayPal, we "inject" our fake HTTP client, which always returns VERIFIED.
Mage::getModel('paypal/ipn')->processIpnRequest($ipnPayload, new MockHttpClient());

I'm sure the DI pattern has plenty of other advantages, but increased testability is the single biggest benefit in my mind.

If you're curious about the solution to this problem, check out the GitHub repo here: https://github.com/bubbleupdev/BUCorefix_Paypalstatus

4
  • 4
    Dependency injection makes code easier to test than code with hardcoded dependencies. Eliminating dependencies from business logic altogether is even better.
    – Ant P
    Commented Nov 13, 2018 at 15:28
  • 1
    And one major way to do what @AntP suggests is via parameterization. The logic to transform results coming back from the database into the object used to fill out a page template (commonly known as a "model") shouldn't even know a fetch is happening; it just needs to get those objects as input.
    – jpmc26
    Commented Nov 13, 2018 at 22:22
  • 4
    @jpmc26 indeed - I tend to harp on about functional core, imperative shell, which is really just a fancy name for passing data into your domain instead of injecting dependencies into it. The domain is pure, can be unit tested with no mocks and then is just wrapped in a shell that adapts things like persistence, messaging etc.
    – Ant P
    Commented Nov 14, 2018 at 8:27
  • 1
    I think the sole focus on testability is harmful to the adoption of DI. It makes it unappealing to people who feel like they either don't need much testing, or think they already have testing under control. I would argue it is virtually impossible to write clean, reusable code without DI. Testing is very far down on the list of benefits and it's disappointing to see this answer ranked 1st or 2nd in every question on DI's benefits. Commented Nov 15, 2018 at 7:57
26

Why (what's even the issue)?

Why should I use dependency injection?

The best mnemonic I found for this is "new is glue": Every time you use new in your code, that code is tied down to that specific implementation. If you repeatedly use new in constructors, you will create a chain of specific implementations. And because you can't "have" an instance of a class without constructing it, you can't separate that chain.

As an example, imagine you're writing a race car video game. You started with a class Game, which creates a RaceTrack, which creates 8 Cars, which each create a Motor. Now if you want 4 additional Cars with a different acceleration, you will have to change every class mentioned, except maybe Game.

Cleaner code

Is this just for cleaner architecture/code

Yes.

However, it might very well seem less clear in this situation, because it's more an example of how to do it. The actual advantage only shows when several classes are involved and is more difficult to demonstrate, but imagine you would have used DI in the previous example. The code creating all those things might look something like this:

List<Car> cars = new List<Car>();
for(int i=0; i<8; i++){
    float acceleration = 0.3f;
    float maxSpeed = 200.0f;
    Motor motor = new Motor(acceleration, maxSpeed);
    Car car = new Car(motor);
    cars.Add(car);
}
RaceTrack raceTrack = new RaceTrack(cars);
Game game = new Game(raceTrack);

Adding those 4 different cars can now be done by adding these lines:

for(int i=0; i<4; i++){
    float acceleration = 0.5f;
    float maxSpeed = 100.0f;
    Motor motor = new Motor(acceleration, maxSpeed);
    Car car = new Car(motor);
    cars.Add(car);
}
  • No changes in RaceTrack, Game, Car, or Motor were necessary - which means we can be 100% sure that we did not introduce any new bugs there!
  • Instead of having to jump between several files, you will be able to see the complete change on your screen at the same time. This is a result of seeing creation/setup/configuration as an own responsibility - it's not the job of a car to build a motor.

Performance considerations

or does this affect performance as a whole?

No. But to be completely honest with you, it might.

However, even in that case, it's such a ridiculously small amount that you don't need to care. If at some point in the future, you have to write code for a tamagotchi with the equivalent of 5Mhz CPU and 2MB RAM, then maybe you might have to care about this.

In 99.999%* of cases it will have a better performance, because you spent less time fixing bugs and more time improving your resource-heavy algorithms.

*completely made up number

Added info: "hard-coded"

Make no mistake, this is still very much "hard-coded" - the numbers are written directly in the code. Not hard-coded would mean something like storing those values in a text file - e.g. in JSON format - and then reading them from that file.

In order to do that, you have to add code for reading a file and then parsing JSON. If you consider the example again; in the non-DI version, a Car or a Motor now has to read a file. That doesn't sound like it makes too much sense.

In the DI version, you would add it to the code setting up the game.

3
  • 4
    Ad hard-coded, there is not really that much difference between code and config file. A file bundled with the application is a source even if you read dynamically. Pulling values from code to data files in json or whatever ‘config’ format helps nothing unless the values should be overridden by the user or depend on the environment.
    – Jan Hudec
    Commented Nov 14, 2018 at 8:11
  • 3
    I actually made a Tamagotchi once on an Arduino (16MHz, 2KB)
    – J_rite
    Commented Nov 14, 2018 at 9:54
  • @JanHudec True. I actually had a longer explanation there, but decided to remove it to keep it shorter and focus on how it relates to DI. There's more stuff which isn't 100% correct; overall the answer is more optimized to push "the point" of DI without it getting too long. Or put differently, this is what I would have wanted to hear when I started out with DI.
    – R. Schmitz
    Commented Nov 14, 2018 at 12:19
18

I was always baffled by dependency injection. It seemed to only exist within Java spheres, but those spheres spoke of it with great reverence. It was one of the great Patterns, you see, which are said to bring order to chaos. But the examples were always convoluted and artificial, establishing a non-problem and then setting out to solve it by making the code more complicated.

It made more sense when a fellow Python dev imparted to me this wisdom: it's just passing arguments to functions. It's barely a pattern at all; more like a reminder that you can ask for something as an argument, even if you could have conceivably provided a reasonable value yourself.

So your question is roughly equivalent to "why should my function take arguments?" and has many of the same answers, namely: to let the caller make decisions.

This comes with a cost, of course, because now you're forcing the caller to make some decision (unless you make the argument optional), and the interface is somewhat more complex. In exchange, you gain flexibility.

So: Is there a good reason you specifically need to use this particular Setting type/value? Is there a good reason calling code might want a different Setting type/value? (Remember, tests are code!)

2
  • 2
    Yep. IoC finally clicked to me when I realised that it's simply about "letting the caller make decisions". That IoC means the control of the component is shifted from the component's author to the component's user. And since at that point I've already had enough gripes with software that thought itself smarter than me, I was instantly sold on the DI approach.
    – Joker_vD
    Commented Nov 14, 2018 at 23:06
  • 1
    "it's just passing arguments to functions. It's barely a pattern at all" This is the only good or even comprehendible explanation of DI I've heard (well most people don't even talk about what it is, but rather its benefits). And then they use complicated jargon like "inversion of control" and "loose coupling" that just require more questions to understand when it's a really simple idea.
    – addison
    Commented May 13, 2019 at 18:34
7

The example you give is not dependency injection in the classical sense. Dependency injection usually refers to passing objects in a constructor or by using "setter injection" just after the object is created, in order to set a value on a field in a newly created object.

Your example passes an object as an argument to an instance method. This instance method then modifies a field on that object. Dependency injection? No. Breaking encapsulation and data hiding? Absolutely!

Now, if the code was like this:

class Profile {
    private $settings;

    public function __construct(Settings $settings) {
        $this->settings = $settings;
    }

    public function deactive() {
        $this->settings->isActive = false;
    }
}

Then I would say you are using dependency injection. The notable difference is a Settings object being passed in to the constructor or a Profile object.

This is useful if the Settings object is expensive or complex to construct, or Settings is an interface or abstract class where multiple concrete implementations exist in order to change run time behavior.

Since you are directly accessing a field on the Settings object rather than calling a method, you can't take advantage of Polymorphism, which is one of the benefits of dependency injection.

It looks like the Settings for a Profile are specific to that profile. In this case I would do one of the following:

  1. Instantiate the Settings object inside the Profile constructor

  2. Pass the Settings object in the constructor and copy over individual fields that apply to the Profile

Honestly, by passing the Settings object in to deactivateProfile and then modifying an internal field of the Settings object is a code smell. The Settings object should be the only one modifying its internal fields.

4
  • 5
    The example you give is not dependency injection in the classical sense. -- It doesn't matter. OO people have objects on the brain, but you're still handing a dependency to something. Commented Nov 13, 2018 at 16:47
  • 1
    When you talk about “in the classical sense”, you are, as @RobertHarvey says, speaking purely in OO terms. In functional programming for example, injecting one function into another (higher order functions) is that paradigm’s classical example of dependency injection.
    – David Arno
    Commented Nov 13, 2018 at 17:11
  • 3
    @RobertHarvey: I guess I was taking too many liberties with "dependency injection." The place people most often think of the term and use the term is in reference to fields on an object being "injected" at the time of object construction, or immediately after in the case of setter injection. Commented Nov 13, 2018 at 19:26
  • @DavidArno: Yes, you are correct. The OP seems to have an object oriented PHP code snippet in the question, so I was answering in that regard only, and not addressing functional programming --- all though with PHP you could ask the same question from the standpoint of functional programming. Commented Nov 13, 2018 at 19:45
7

I know I'm coming late to this party but I feel an important point is being missed.

Why should I do this:

class Profile {
    public function deactivateProfile(Setting $setting)
    {
        $setting->isActive = false;
    }
}

You shouldn't. But not because Dependency Injection is a bad idea. It's because this is doing it wrong.

Lets look at this things using code. We're going to do this:

$profile = new Profile();
$profile->deactivateProfile($setting);

when we get about the same thing out of this:

$setting->isActive = false; // Deactivate profile

So of course it seems like a waste of time. It is when you do it this way. This is not the best use of Dependency Injection. It's not even the best use of a class.

Now what if instead we had this:

$profile = new Profile($setting);

$application = new Application($profile);

$application.start();

And now the application is free to activate and deactivate the profile without having to know anything in particular about the setting that it's actually changing. Why is that good? In case you need to change setting. The application is walled off from those changes so you're free to go nuts in a safe contained space without having to watch everything break as soon as you touch something.

This follows the separate construction from behavior principle. The DI pattern here is a simple one. Build everything you need at as low a level as you can, wire them together, then start all the behavior ticking with one call.

The result is you have a separate place to decide what connects to what and a different place to manage what says what to whatever.

Try that on something you have to maintain over time and see if it doesn't help.

0
6

As a customer, when you hire a mechanic to do something to your car, do you expect that mechanic to build a car from scratch only to then work with it? No, you give the mechanic the car you want them to work on.

As the garage owner, when you instruct a mechanic to do something to a car, do you expect the mechanic to create his own screwdriver/wrench/car parts? No, you provide the mechanic with the parts/tools he needs to use

Why do we do this? Well, think about it. You're a garage owner who wants to hire someone to become your mechanic. You will teach them to be a mechanic (= you will write the code).

What's going to be easier:

  • Teach a mechanic how to attach a spoiler to a car using a screwdriver.
  • Teach a mechanic to create a car, create a spoiler, created a screwdriver and then attach the newly created spoiler to the newly created car with the newly created screwdriver.

There are massive benefits to not having your mechanic create everything from scratch:

  • Obviously, training (= development) is dramatically shortened if you just supply your mechanic with existing tools and parts.
  • If the same mechanic has to perform the same job multiple times, you can make sure he reuses the screwdriver instead of always throwing the old one out and creating a new one.
  • Additionally, a mechanic who has learned to create everything will need to be much more of an expert, and thus will expect a higher wage. The coding analogy here is that a class with many responsibilities is much harder to maintain than a class with a single strictly defined responsibility.
  • Additionally, when new inventions hit the market and spoilers are now being made from carbon instead of plastic; you will have to retrain (= redevelop) your expert mechanic. But your "simple" mechanic won't have to be retrained as long as the spoiler can still be attached in the same way.
  • Having a mechanic who doesn't rely on a car that they've built themselves means that you have a mechanic who is able to handle any car they may receive. Including cars that didn't even exist yet at the time of training the mechanic. However, your expert mechanic will not be able to build newer cars that have been created after their training.

If you hire and train the expert mechanic, you're going to end up with an employee who costs more, takes more time to perform what ought to be a simple job, and will perpetually need to be retrained whenever one of their many responsibilities need to be updated.

The development analogy is that if you use classes with hardcoded dependencies, then you're going to end up with hard to maintain classes which will need continual redevelopment/changes whenever a new version of the object (Settings in your case) is created, and you'll have to develop internal logic for the class to have the ability to create different types of Settings objects.

Furthermore, whoever consumes your class is now also going to have to ask the class to create the correct Settings object, as opposed to simply being able to pass the class any Settings object it wishes to pass. This means additional development for the consumer to figure out how to ask the class to create the right tool.


Yes, dependency inversion takes a bit more effort to write instead of hardcoding the dependency. Yes, it's annoying to have to type more.

But that is the same argument as choosing to hardcode literal values because "declaring variables takes more effort". Technically correct, but the pro's outweigh the cons by several orders of magnitude.

The benefit of dependency inversion is not experienced when you create the first version of the application. The benefit of dependency inversion is experienced when you need to change or extend that initial version. And don't trick yourself into thinking that you will get it right the first time and won't need to extend/change the code. You will have to change things.


does this affect performance as a whole?

This does not affect runtime performance of the application. But it massively impacts the development time (and therefore performance) of the developer.

14
  • 2
    If you removed your comment, "As a minor comment, your question focuses on dependency inversion, not dependency injection. Injection is one way to do inversion, but it's not the only way.", this would be an excellent answer. Dependency inversion can be achieved via either injection or locator/globals. The question's examples relate to injection. So the question is about dependency injection (as well as dependency inversion).
    – David Arno
    Commented Nov 13, 2018 at 12:21
  • 12
    I think the whole car thing is a bit belaboured and confusing
    – Ewan
    Commented Nov 13, 2018 at 13:16
  • 4
    @Flater, part of the problem is no one really seems to agree on what the difference between dependency injection, dependency inversion and inversion of control are. One thing is for certain though, a "container" most certainly isn't needed to inject a dependency into a method or constructor. Pure (or poor man's) DI specifically describes manual dependency injection. It's the only dependency injection I personally use as I dislike the "magic" associated with containers.
    – David Arno
    Commented Nov 13, 2018 at 13:29
  • 1
    @Flater The problem with the example is that you would almost certainly not model any problem in code this way. It is thus unnatural, forced, and adds more questions than answers. That makes it more likely to mislead and confuse than to demonstrate.
    – jpmc26
    Commented Nov 13, 2018 at 16:50
  • 2
    @gbjbaanb: At no point does my answer even remotely delve into polymorphism. There is no inheritance, interface implementation, or anything resembling the up/downcasting of types. You are completely misreading the answer.
    – Flater
    Commented Nov 15, 2018 at 12:17
0

Like all patterns, it is very valid to ask "why" to avoid bloated designs.

For dependency injection, this is easily seen by thinking of the two, arguably, most important facets of OOP design...

Low coupling

Coupling in computer programming:

In software engineering, coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules.

You want to achieve low coupling. Two things being strongly coupled means that if you change the one, you very likely have to change the other. Bugs or restrictions in the one likely will induce bugs/restrictions in the other; and so on.

One class instantiating objects of the others is a very strong coupling, because the one needs to know about the existence of the other; it needs to know how to instantiate it (which arguments the constructor needs), and those arguments need to be available when calling the constructor. Also, depending on whether the language needs explicit deconstruction (C++), this will introduce further complications. If you introduce new classes (i.e., a NextSettings or whatever), you have to go back to the original class and add more calls to their constructors.

High cohesion

Cohesion:

In computer programming, cohesion refers to the degree to which the elements inside a module belong together.

This is the other side of the coin. If you look at one unit of code (one method, one class, one package etc.), you want to have all code inside that unit to have as few responsibilities as possible.

A basic example for this would be the MVC pattern: you clearly separate the domain model from the view (GUI) and a control layer that glues those together.

This avoids code bloat where you get large chunks that do lots of different things; if you wish to change some part of it, you have to keep track of all the other features as well, trying to avoid bugs, etc.; and you quickly program yourself into a hole where it's very hard to get out.

With dependency injection, you delegate the creation or keeping track of, well, dependencies to whatever classes (or configuration files) which implement your DI. Other classes will not care much about what exactly is going on - they will be working with some generic interfaces, and have no idea what the actual implementation is, which means they are not responsible for the other stuff.

-1

You should use techniques to solve the problems they're good at solving when you have those problems. Dependency inversion and injection are no different.

Dependency inversion or injection is a technique that allows your code to decide on what implementation of a method gets called at run time. This maximizes the benefits of late binding. The technique is necessary when the language does not support run time replacement of non-instance functions. For example, Java lacks a mechanism to replace calls to a static method with calls to a different implementation; contrast with Python, where all that's necessary to replace the function call is to bind the name to a different function (reassign the variable holding the function).

Why would we want to vary the implementation of the function? There's a two main reasons:

  • We want to use fakes for testing purposes. This allows us to test a class that depends on a database fetch without actually connecting to the database.
  • We need to support multiple implementations. For example, we might need to set up a system that supports both MySQL and PostgreSQL databases.

You may also want to take note of inversion of control containers. This is a technique that is intended to help you avoid huge, tangled construction trees that look like this pseudocode:

thing5 =  new MyThing5();
thing3 = new MyThing3(thing5, new MyThing10());

myApp = new MyApp(
    new MyAppDependency1(thing5, thing3),
    new MyAppDependency2(
        new Thing1(),
        new Thing2(new Thing3(thing5, new Thing4(thing5)))
    ),
    ...
    new MyAppDependency15(thing5)
);

It lets you register your classes and then does the construction for you:

injector.register(Thing1); // Yes, you'd need some kind of actual class reference.
injector.register(Thing2);
...
injector.register(MyAppDepdency15);
injector.register(MyApp);

myApp = injector.create(MyApp); // The injector fills in all the construction parameters.

Note that it's simplest if the classes registered can be stateless singletons.

Word of caution

Note that dependency inversion should not be your go-to answer for decoupling logic. Look for opportunities to use parameterization instead. Consider this pseudocode method for example:

myAverageAboveMin()
{
    dbConn = new DbConnection("my connection string");
    dbQuery = dbConn.makeQuery();
    dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
    dbQuery.setParam("min", 5);
    dbQuery.Execute();
    myData = dbQuery.getAll();
    count = 0;
    total = 0;
    foreach (row in myData)
    {
        count++;
        total += row.x;
    }

    return total / count;
}

We could use dependency inversion for some parts of this method:

class MyQuerier
{
    private _dbConn;

    MyQueries(dbConn) { this._dbConn = dbConn; }

    fetchAboveMin(min)
    {
        dbQuery = this._dbConn.makeQuery();
        dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
        dbQuery.setParam("min", min);
        dbQuery.Execute();
        return dbQuery.getAll();
    }
}


class Averager
{
    private _querier;

    Averager(querier) { this._querier = querier; }

    myAverageAboveMin(min)
    {
        myData = this._querier.fetchAboveMin(min);
        count = 0;
        total = 0;
        foreach (row in myData)
        {
            count++;
            total += row.x;
        }

        return total / count;
    }

But we shouldn't, at least not completely. Notice that we've created a stateful class with Querier. It now holds a reference to some essentially global connection object. This creates problems such as difficulty in understanding the overall state of the program and how different classes coordinate with each other. Notice also that we're forced to fake out the querier or the connection if we want to test the averaging logic. Further A better approach would be to increase parameterization:

class MyQuerier
{
    fetchAboveMin(dbConn, min)
    {
        dbQuery = dbConn.makeQuery();
        dbQuery.Command = "SELECT * FROM MY_DATA WHERE x > :min";
        dbQuery.setParam("min", min);
        dbQuery.Execute();
        return dbQuery.getAll();
    }
}


class Averager
{
    averageData(myData)
    {
        count = 0;
        total = 0;
        foreach (row in myData)
        {
            count++;
            total += row.x;
        }

        return total / count;
    }

class StuffDoer
{
    private _querier;
    private _averager;

    StuffDoer(querier, averager)
    {
        this._querier = querier;
        this._averager = averager;
    }

    myAverageAboveMin(dbConn, min)
    {
        myData = this._querier.fetchAboveMin(dbConn, min);
        return this._averager.averageData(myData);
    }
}

And the connection would be managed at some even higher level that's responsible for the operation as a whole and knows what to do with this output.

Now we can test the averaging logic completely independently of the querying, and what's more we can use it in a wider variety of situations. We might question whether we even need the MyQuerier and Averager objects, and maybe the answer is that we don't if we don't intend to unit test StuffDoer, and not unit testing StuffDoer would be perfectly reasonable since it's so tightly coupled to the database. It might make more sense to just let integration tests cover it. In that case, we might be fine making fetchAboveMin and averageData into static methods.

7
  • 2
    "Dependency injection is a technique that is intended to help you avoid huge, tangled construction trees...". Your first example after this claim is an example of pure, or poor man's, dependency injection. The second is an example of using an IoC container to "automate" the injection of those dependencies. Both are examples of dependency injection in action.
    – David Arno
    Commented Nov 13, 2018 at 16:13
  • @DavidArno Yeah, you're right. I've adjusted the terminology.
    – jpmc26
    Commented Nov 13, 2018 at 16:47
  • There's a third main reason to vary the implementation, or at least design the code assuming that the implementation can vary: it incentivizes developer to have loose coupling and avoid writing code that will be hard to change should a new implementation be implemented at some time in the future. And while that may not be a priority in some projects (e.g. knowing that the application will never be revisited after its initial release), it will be in others (e.g. where the company business model specifically tries to offer extended support/extension of their applications).
    – Flater
    Commented Nov 15, 2018 at 7:06
  • @Flater Dependency injection still results in strong coupling. It ties the logic to a particular interface and requires the code in question to know about whatever that interface does. Consider code that transforms results fetched from a DB. If I use DI to separate them, then the code still needs to know that a DB fetch happens and invoke it. The better solution is if the transformation code doesn't even know a fetch is happening. The only way you can do that is if the results of the fetch are passed in by a caller, rather than injecting the fetcher.
    – jpmc26
    Commented Nov 15, 2018 at 16:47
  • 1
    @jpmc26 But in your (comment) example the database didn't even need to be a dependency to begin with. Of course avoiding dependencies is better for loose coupling, but DI focuses on implementing dependencies that are needed.
    – Flater
    Commented Nov 15, 2018 at 16:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.