Dependency Injection (DI)

burnwholeInternet and Web Development

Feb 5, 2013 (4 years and 6 months ago)

511 views

Dependency Injection (DI)

Or “Inversion of Control”


Dave
Elliman

The Problem with Software… (why it rots)


Coupling


i.e. dependencies between classes


These are examples of coupling:


fred

= new Student();



TIGHT coupling


public class Student extends Person {…

TIGHT coupling


fred

= new Person();



A BIT LOOSER


personFactory

= new
PersonFactory
();

MODERATE


Fred =
personFactory.createPerson
();

might use Context


Most Java APIs use an abstract factory like this


Can we do better?



Yes we can!


Avoid using new most of the time


Pass objects that are needed in using a
constructor


These objects simply need to implement
the methods that are called


In Objective
-
C/Python and Ruby the
objects could be of any type

Where do we create the objects and decide
which to pass as arguments?


This can be done in a container


Specified in XML (or using annotations)


Can be decided at run time


We have a model like:


Choose objects and create them


Wire up constructor calls


Run program


This is now very loosely coupled


Calling objects only need to know that an object passed can
respond to a known method call


Can replace objects with stubs, test objects, mock objects etc
.


Background Laws


SRP


Single Responsibility Principle


A class should have one and only one reason to change


OCP


The Open
-
Closed Principle


Code should be open to extension and closed to modification




DI is a great help in doing what seems paradoxical at first sight

The Law of Demeter


Only talk to your friends….


Not to friends of friends


This
also limits coupling


a.getX
().
getY
() is a violation


So is…
serviceLocator.getService
()


This needs a bit of
explaining

PCTIP: Prefer Composition to
Inheritance Principle


Why?


Because it is a looser form of coupling

Design patterns are neat designs
based on these principles

GOF Book (Gang of Four)

Gamma, Helm, Johnson & Vlissides

03/08/10

G53ELC

4

What
is Dependency Injection?


DI is all about wiring up objects or plumbing if you prefer


It is about ensuring loose coupling and fits well with design
patterns


Design to an interface and then inject the actual class at run
time


This is what inheritance is really
for in Java, C# and C++


You don’t even need to use inheritance in dynamic languages
such as Python, Ruby, Groovy, Objective
-
C etc.

Martin Fowler


Martin Fowler has been hugely influential in popularising this
design pattern


http://martinfowler.com
/


The best description of the material in this lecture is here:


http://martinfowler.com/articles/injection.html


Rod Johnson is the other guru
-

the man behind Spring


http://www.springsource.com/about/management

11

03/08/10

G53ELC

5

Web App

Stock Quotes

Authenticator

Error Handler

Logger

Database

This example was originally created by Jim Weirich in Ruby on his blog.

An EXAMPLE

Remember the old way

public class
WebApp

{


public
WebApp
()


{



quotes = new
StockQuotes
();



authenticator = new Authenticator();



database = new Database();



logger = new Logger();



errorHandler

= new
ErrorHandler
();


}


// More code here...

}

What about the child objects?


How does the StockQuotes find the Logger?


How does the Authenticator find the database?


Suppose you want to use a TestingLogger
instead? Or a MockDatabase?

Service Locator Interface

public interface ILocator

{

TObject Get<TObject>();

}

G53ELC

Service Locator Example

public class
MyLocator

:
ILocator

{

protected Dictionary<Type, object>
dict

=

new Dictionary<
Type,object
>();


public
MyLocator
()

{


dict.Add
(
typeof
(
ILogger
), new Logger());


dict.Add
(
typeof
(
IErrorHandler
),



new
ErrorHandler
(this));


dict.Add
(
typeof
(
IQuotes
), new
StockQuotes
(this));


dict.Add
(
typeof
(
IDatabase
), new Database(this));


dict.Add
(
typeof
(
IAuthenticator
),



new Authenticator(this));


dict.Add
(
typeof
(
WebApp
), new
WebApp
(this));

}

}

StockQuotes

with Locator

public class StockQuotes

{


public StockQuotes(ILocator locator)


{



errorHandler =



locator.Get<IErrorHandler>();



logger = locator.Get<ILogger>();


}



// More code here...

}

Good things


Classes are decoupled from explicit imlementation types


Easy to externalise the configuration

12

Dependency Injection Containers


Gets rid of the dependency on the
ILocator


Object is no longer responsible for finding its dependencies


The container does it for you

Then what?


Write your objects the way you want


Setup the container


Ask the container for objects


The container creates objects for you and configures
dependencies

Setting Up the Container (XML)

<
DIContainer
>


<
TypeMap

from=“
ILogger
” to=“Logger” />


<
TypeMap

from=“
IDatabase
” to=“Database” />


<
TypeMap

from=“
IErrorHandler
” to=“
ErrorHandler
” />


<
TypeMap

from=“
IQuotes
” to=“
StockQuotes
” />


<
TypeMap

from=“
IAuthenticator
” to=“Authenticator” />

</
DIContainer
>


Java/Groovy


Pico Container


Spring
Framework


Guice

(Google)


HiveMind



Ruby


Rico


Copland


Python


SpringPython


Strappy



.NET


Pico.NET


Spring.NET


p&p

Object Builder

DI Frameworks

Things are usually
easier in Groovy, Python
or Ruby


This is Strappy
-

Register a new class

from strappy import Component



Create an instance of a registered class

from strappy import Component


@Component

class Spam(object):


def

getSpam
(self):


return 'spam'


@Component

class Eggs(object):


def

getEggs
(self):


return 'eggs
'


Inject dependencies

from strappy import
Autowired
, Component


@Component

class Foo(object):


@
Autowired


def

__
init
__(self, spam, eggs):


self.spam

= spam


self.eggs

= eggs



def

eat(self):


print 'I am eating "%s" and "%s"' %



(
self.spam.getSpam
(),
self.eggs.getEggs
())



Using the autowired components

from strappy import Context



ctx

= Context()

foo =
ctx.getObject
('Foo')

foo.eat
()


We have
loose coupling

Code is closed to modification and open to extension
(OCP)