The Why and How of Dependency Injection

TCCC XX

CODE: https://github.com/seanmacavaney/tccc20

About Me

Sean MacAvaney

macavaney.us    @SeanMacAvaney •  sean.macavaney@gmail.com

  • Software Engineering student at graduate from Milwaukee School of Engineering
  • Software Developer at Integrated Inventory Technology
  • Interest in web technologies

About You

Overview

  • What?
  • Why?
  • How?
    • Live Coding!

What?

Dependency Injection is a 25-dollar term for a 5-cent concept.

"

- James Shore
<http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html>

Dependency injection means giving an object its instance variables. Really. That's it.

Dependency Injection is a software design pattern that implements inversion of control for resolving dependencies. [...] Passing [dependencies] to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

- Wikipedia
<https://en.wikipedia.org/wiki/Dependency_injection>

"

Injected or Not Injected?

class Aardvark {
  private string name;
  private double weightLbs;
  
  public Aardvark(string name, double weightLbs) {
    this.name = name;
    this.weightLbs = weightLbs;
  }
  
  ...
}


class AardvarkDriver {
  public static void main(String[] args) {
    Aardvark lenny = new Aardvark("Lenny", 130);
    lenny.DoAardvarkStuff();
  }
}

Injected or Not Injected?

class BreweryService {
  private String name;
  private IMarketingStrategy marketing = new HipsterMarketingStrategy();
  private IProductionStrategy production = new MicrobrewingStrategy();
  
  ...
}






class BreweryDriver {
  public static void main(String[] args) {
    BreweryService mkeBrewing = new BreweryService();
    mkeBrewery.Brew();
  }
}

Injected or Not Injected?

class ConferenceService {
  private IAttendeeRepository attendeeRepo;
  
  public ConferenceService(IDatabaseFactory db) {
    this.attendeeRepo = new AttendeeRepository(db);
  }

   ...
}




class ConferenceDriver {
  public static void main(String[] args) {
    IDatabaseFactory db = new DatabaseFactory();
    ConferenceService tccc = new ConferenceService(db);
    tccc.EmailAttendees();
  }
}

Injected or Not Injected?

class CongressService {
  private ISenateService senateService;
  private IHouseService houseService;
  
  public CongressService(ISenateService senateService, IHouseService houseService) {
    this.senateService = senateService;
    this.houseService = houseService;
  }
  ...
}



class CongressDriver {
  public static void main(String[] args) {
    IDatabaseFactory db = new DatabaseFactory();
    ISenateServiceService senate = new SenateService(db);
    IHouseService house = new HouseService(db);
    CongressService congress = new CongressService(senate, house);
    congress.DoNothing();
  }
}

Injected or Not Injected?

(function () {
  
  window.ShoeshineService = function () {
    this.shineTheShoes = function (shoes) {
      var polish = window.polishService.getPolish();
      ...
    };
  };
  
})();

Injected or Not Injected?

angular.module('secret', ['ngRoute', 'firebase'])

.service('nsa', function($q, prism, fbi, dhs) {
  var self = this;
  this.spy = function () {
    ...
  };
  ...
});

Why?

Coupling.

Knowledge of Construction

class BreweryController {
  private DatabaseConnection db;
  private BreweryRepository repo;
  
  public BreweryController() {
    this.db = new DatabaseConnection("Server=myServerAddress;Database=myDatabase;");
    this.repo = new BreweryRepository(this.db);
  }
  
  ...
}

Knowledge of Construction

class BreweryController {
  private DatabaseConnection db;
  private BreweryRepository repo;
  
  public BreweryController(DatabaseConnection db, BreweryRepository repo) {
    this.db = db;
    this.repo = repo;
  }
  
  ...
}

Knowledge of Implementation

class Brewery {
  private String name;
  private IMarketingStrategy marketing = new HipsterMarketingStrategy();
  private IProductionStrategy production = new MicrobrewingStrategy();
  
  ...
}

Test Environment

class Brewery {
  private String name;
  private IMarketingStrategy marketing;
  private IProductionStrategy production;
  
  public Brewery(String name, IMarketingStrategy marketing, IProductionStrategy production) {
    this.name = name;
    this.marketing = marketing;
    this.production = production;
  }
  
  ...
}

class TestBrewery {
  void test() {
    ...
    IMarketingStrategy strategy = new FakeMarketingStrategy();
    Brewery brewery = new Brewery("test", strategy);
    ...
  }
}

Runtime Configuration

interface IMarketingService { ... }
class HipsterMarketingService : IMarketingService { ... }
class BroMarketingService : IMarketingService { ... }
class ClassyMarketingService : IMarketingService { ... }
<dependencies>
  <dependency for="IMarketingService" use="HipsterMarketingService" />
  ...
</dependencies>

How?

Component 1
Component 2
Component n

???

Dependency Resolver

Dependency Resolver

class CongressService {
  private ISenateService senateService;
  private IHouseService houseService;
  
  public CongressService(ISenateService senateService, IHouseService houseService) {
    this.senateService = senateService;
    this.houseService = houseService;
  }
  ...
}



class CongressDriver {
  public static void main(String[] args) {
    IDatabaseFactory db = new DatabaseFactory();
    ISenateServiceService senate = new SenateService(db);
    IHouseService house = new HouseService(db);
    CongressService congress = new CongressService(senate, house);
    congress.DoNothing();
  }
}

Dependency Resolver

interface IDependencyResolver {
  T GetService<T>();
}

class CongressService {
  private ISenateService senateService;
  private IHouseService houseService;
  
  public CongressService(ISenateService senateService, IHouseService houseService) {
    this.senateService = senateService;
    this.houseService = houseService;
  }
  ...
}



class CongressDriver {
  public static void main(String[] args) {
    IDependencyResolver resolver = new DependencyResolver();
    CongressService congress = resolver.GetService<CongressService>();
    congress.DoNothing();
  }
}

Reflection.

Reflection is the ability of a program to examine and modify its own structure and behavior at runtime.”

- Wikipedia
<https://en.wikipedia.org/wiki/Reflection_(computer_programming)>

"

Functions

  • Inspection

  • Invocation

  • Installation

Applications

  • Serialization

  • Adaptation

  • Extension

Inspection (C#)

static void main(String[] args) {
  Aardvark lenny = new Aardvark("Lenny");
  PrintTypeInfo(lenny);
}

static void PrintTypeInfo(object o) {
  Type t = o.GetType();
  System.Console.WriteLine(t.ToString());
  System.Console.WriteLine("Properties:");
  foreach (var prop in t.GetProperties()) {
    System.Console.WriteLine(" - " + prop.Name + ": "
      + prop.PropertyType.ToString());
  }
}

>> MyApp.Aardvark
>> Properties:
>>  - Name: System.String
>>  - WeightLbs: System.double

Invocation (C#)

static void main(String[] args) {
  Aardvark lenny = new Aardvark("Lenny");
  System.Console.WriteLine("before: " + lenny.Name);
  OverrideName(lenny);
  System.Console.WriteLine("after: " + lenny.Name);
}

static void OverrideName(object o) {
  PropertyInfo nameProp = 
    o.GetType().GetProperties().Where(p => p.Name == "Name");
  nameProp.SetValue(o, "Jessie");
}

>> before: Lenny
>> after: Jessie

Installation (C#)

static void main(String[] args) {
  Type catClass = MakeType("Cat");
  var catInstance = Activator.CreateInstance(catClass);
  System.Console.WriteLine(catInstance.GetType().ToString());
}

static Type MakeType(string name) {
  AssemblyName assembly = new AssemblyName("TmpAssembly");
  AppDomain appDomain = System.Threading.Thread.GetDomain();
  AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
  ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name);
  TypeBuilder typeBuilder = moduleBuilder.DefineType(name, ..., typeof(System.Object));
  return typeBuilder.CreateType();
}

>> Cat

JS Examples

// Inspection
var cat = {
  name: "Fritz",
  lives: 9
};
var catProps = Reflect.ownKeys(cat);
console.log(catProps);

>> ["name", "lives"]
// Invocation
purr.apply(cat, ["15 seconds", 98]);

>> Fritz is purring for 15 seconds at 98%
// Installation
function purr(duration, intensity) {
  console.log(this.name + "is purring for " + duration + " at " + intensity + "%");
};
cat.purr = purr;
cat.purr("8 seconds", 50);

>> Fritz is purring for 8 seconds at 50%

Demo

ASP.NET MVC Example

Questions?

https://slides.com/seanm/tccc20

http://speakerrate.com/talks/66491

Additional Resources

http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html

http://stackoverflow.com/questions/130794

https://msdn.microsoft.com/en-us/library/ff921152.asp

They Why and How of Dependency Injection

By seanm

They Why and How of Dependency Injection

  • 1,010