title: Where is the Balance Between Dependency Injection and Abstraction?
body: <p>Many Architects and Engineers recommend <a href="https://en.wikipedia.org/wiki/Dependency_injection" rel="nofollow">Dependency Injection</a> and other <a href="https://en.wikipedia.org/wiki/Inversion_of_control" rel="nofollow">Inversion of Control</a> patterns as a way to <a href="https://www.youtube.com/watch?v=XcT4yYu_TTs" rel="nofollow">improve the testability of your code</a>. There's no denying that Dependency Injection makes code more testable, <em>however</em>, isn't it also a completing goal to <a href="https://en.wikipedia.org/wiki/Abstraction_(computer_science)" rel="nofollow">Abstraction</a> in general?</p>
  
  <p>I feel conflicted! I wrote an example to illustrate this; it's not super-realistic and I wouldn't design it this way, but I needed a quick and simple example of a class structure with multiple dependencies. The first example is without Dependency Injection, and the second uses Injected Dependencies. </p>
  
  <p><strong>Non-DI Example</strong></p>
  
  
  
  <pre class="lang-java prettyprint-override"><code>package com.stackoverflow.di;
  
  
  public class EmployeeInventoryAnswerer()
  {
      /* In reality, at least the store name and product name would be
       * passed in, but this example can't be 8 pages long or the point
       * may be lost.
       */
      public void myEntryPoint()
      {
          Store oaklandStore = new Store('Oakland, CA');
          StoreInventoryManager inventoryManager = new StoreInventoryManager(oaklandStore);
          Product fancyNewProduct = new Product('My Awesome Product');
  
          if (inventoryManager.isProductInStock(fancyNewProduct))
          {
              System.out.println("Product is in stock.");
          }
      }
  }
  
  
  public class StoreInventoryManager
  {
      protected Store store;
      protected InventoryCatalog catalog;
  
      public StoreInventoryManager(Store store)
      {
          this.store = store;
          this.catalog = new InventoryCatalog();
      }
  
      public void addProduct(Product product, int quantity)
      {
          this.catalog.addProduct(this.store, product, quantity);
      }
  
      public boolean isProductInStock(Product product)
      {
          return this.catalog.isInStock(this.store, this.product);
      }
  }
  
  
  public class InventoryCatalog
  {
      protected Database db;
  
      public InventoryCatalog()
      {
          this.db = new Database('productReadWrite');
      }
  
  
      public void addProduct(Store store, Product product, int initialQuantity)
      {
          this.db.query(
              'INSERT INTO store_inventory SET store_id = %d, product_id = %d, quantity = %d'
          ).format(
              store.id, product.id, initialQuantity
          );
      }
  
      public boolean isInStock(Store store, Product product)
      {
          QueryResult qr;
  
          qr = this.db.query(
              'SELECT quantity FROM store_inventory WHERE store_id = %d AND product_id = %d'
          ).format(
              store.id, product.id
          );
  
          if (qr.quantity.toInt() &gt; 0)
          {
              return true;
          }
  
          return false;
      }
  }
  </code></pre>
  
  <p><strong>Dependency-Injected Example</strong></p>
  
  <pre class="lang-java prettyprint-override"><code>package com.stackoverflow.di;
  
  
  public class EmployeeInventoryAnswerer()
  {
      public void myEntryPoint()
      {
          Database db = new Database('productReadWrite');
          InventoryCatalog catalog = new InventoryCatalog(db);
  
          Store oaklandStore = new Store('Oakland, CA');
          StoreInventoryManager inventoryManager = new StoreInventoryManager(oaklandStore, catalog);
          Product fancyNewProduct = new Product('My Awesome Product');
  
          if (inventoryManager.isProductInStock(fancyNewProduct))
          {
              System.out.println("Product is in stock.");
          }
      }
  }
  
  public class StoreInventoryManager
  {
      protected Store store;
      protected InventoryCatalog catalog;
  
      public StoreInventoryManager(Store store, InventoryCatalog catalog)
      {
          this.store = store;
          this.catalog = catalog;
      }
  
      public void addProduct(Product product, int quantity)
      {
          this.catalog.addProduct(this.store, product, quantity);
      }
  
      public boolean isProductInStock(Product product)
      {
          return this.catalog.isInStock(this.store, this.product);
      }
  }
  
  
  public class InventoryCatalog
  {
      protected Database db;
  
      public InventoryCatalog(Database db)
      {
          this.db = db;
      }
  
  
      public void addProduct(Store store, Product product, int initialQuantity)
      {
          this.db.query(
              'INSERT INTO store_inventory SET store_id = %d, product_id = %d, quantity = %d'
          ).format(
              store.id, product.id, initialQuantity
          );
      }
  
      public boolean isInStock(Store store, Product product)
      {
          QueryResult qr;
  
          qr = this.db.query(
              'SELECT quantity FROM store_inventory WHERE store_id = %d AND product_id = %d'
          ).format(
              store.id, product.id
          );
  
          if (qr.quantity.toInt() &gt; 0)
          {
              return true;
          }
  
          return false;
      }
  }
  </code></pre>
  
  <p><em>(Please feel to make my example better if you have any ideas! It might not be the best example.)</em></p>
  
  <p>In my example, I feel that Abstraction has been completely violated by <code>EmployeeInventoryAnswerer</code> having knowledge of underlying implementation details of <code>StoreInventoryManager</code>.</p>
  
  <p>Shouldn't <code>EmployeeInventoryAnswerer</code>have the perspective of, <em>"Okay, I'll just grab a <code>StoreInventoryManager</code>, give it the name of the product the customer is looking for, and what store I want to check, and it will tell me if the product is in stock."</em>? Shouldn't it not know a single thing about <code>Database</code>s or <code>InventoryCatalog</code>s, as from its perspective, that's an implementation detail it need not concern itself with?</p>
  
  <p>So, where's the balance between testable code with injected dependencies, and information-hiding as a principal of abstraction? Even if the middle-classes are merely passing-through dependencies, the constructor signature alone reveals irrelevant details, right?</p>
  
  <p><em>More realistically</em>, let's say this a long-running background application processing data from a DBMS; at what "layer" of the call-graph is it appropriate to create and pass around a database connector, while still making your code testable without a running DBMS?</p>
  
  <p>I'm very interested in learning about both OOP theory and practicality here, as well as clarifying what seems to be a paradox between DI and Information Hiding/Abstraction.</p>