De­pen­den­cy In­jec­tion

De­pen­den­cy In­jec­tion is solving a funda­men­tal prob­lem in soft­ware ar­chi­tec­ture. How do I de­cou­ple an piece of code that does some­thing from the code that does it?

Con­sid­er a ser­vice that down­load­s a file and com­putes the hash of it. One ob­vi­ous way of do­ing that is sim­ply call­ing the we­b ser­vices to down­load the file and the Hash­Ser­vice to hash the file:

public class WebHashService {
  public Hash getHash(Url url) {
    String file = new WebService().download(url);
    return new HashService().hash(file)
  }
}

How­ev­er this ap­proach has mul­ti­ple draw­back­s. First, it is hard to test. We can­not test it with­out down­load­ing a file from the in­ter­net. Not on­ly is this s­low, but y­our test might break if the web­page changes! Sec­ond, it is hard to change. The file is closed for ex­ten­sion, and not closed for mod­i­fi­ca­tion (see SOL­ID).

We should re­ly on ab­strac­tion­s not con­cre­tion­s. We can do that by in­ject­ing our de­pen­den­cies in­stead of defining them in the file.

public class WebHashService {

  // Define the Downloader interface
  public static interface Downloader {
    String download(Url url);
  }
  private final Downloader downloader;
  
  // Define the Hasher interface
  public static interface Hasher {
    Hash hash(String url);
  }
  private final Hasher hasher;

  // Now we can ask the caller to inject the dependencies.
  public WebHashService(Downloader downloader, Hasher hasher) {
    this.downloader = downloader;
    this.hasher = hasher;
  }

  // We can call the abstract 
  public Hash getHash(Url url) {
    String file = downloader.download(url);
    return hasher.hash(file)
  }
}

Now test­ing the code is triv­ial:

@Test
void download_and_hash() {

  // Mock a downloader
  Downloader downloader = (Url url) -> { 
    assertEquals("google.com", url);
    return "<web>"
  }
  
  // Mock a hasher
  Hasher hasher = (String string) -> { 
    assertEquals("<web>", string);
    return new Hash("thehash")
  }

  // Inject the dependencies
  WebHashService ws = new WebHashService(downloader, hasher);

  // Check that we get the right hash.
  assertEquals(new Hash("thehash"), ws.getHash("google.com"));

}

Do­ing this in y­our code is some­times refer­d to as De­pen­den­cy In­ver­sion, be­cause we are chang­ing di­rec­tion of the de­pen­den­cies.

To read more take a look at