28
Dependency Injection Robert Walter MSI 3 EMFW WS 08/09 13.11.2008

Dependency Injection - Hochschule Konstanz · 13.11.2008 Robert Walter ‐MSI 3 5 / 46 References Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern

Embed Size (px)

Citation preview

Dependency

Injection

Robert WalterMSI 3EMFW WS 08/09

13.11.2008

13.11.2008Robert Walter ‐MSI 3 2

/ 46

Overview

• Outline• References• Necessity• Simple Example Design• Summary

13.11.2008Robert Walter ‐MSI 3 3

/ 46

Outline

• Not focussing on– Different frameworks

(Spring vs. PicoContainer vs. Avalon vs. Guice vs. …)– DI vs. Service Locator Pattern

• Instead– Simple example design– Different approaches

• Factory• Dependency Injection by Hand• Dependency Injection with Google Guice

13.11.2008Robert Walter ‐MSI 3 4

/ 46

Overview

• Outline• References• Necessity• Simple Example Design• Summary

13.11.2008Robert Walter ‐MSI 3 5

/ 46

ReferencesMartin Fowler. Inversion of Control Containers and the DependencyInjection pattern. 2004. URL: http://martinfowler.com/articles/injection.html

Bob Lee. Java on Guice: Dependency Injection, the Java Way. 2007. URL: http://video.google.com/videosearch?q=Guice&emb=0#URL: http://crazybob.org/

Framework‐Homepages: PicoContainer, Spring, Google Guice, …

Wikipedia

13.11.2008Robert Walter ‐MSI 3 6

/ 46

Overview

• Outline• References• Necessity• Simple Example Design• Summary

13.11.2008Robert Walter ‐MSI 3 7

/ 46

Necessity

• Dependencies between objects lead to several problems regarding– Reusability– Flexibility– Scalability– Maintainability– (Unit)‐Testing

• Use of interfaces alone is not enough (as some design patterns are not)

13.11.2008Robert Walter ‐MSI 3 8

/ 46

Misusing an Interface ( Fowler)

<<interface>>MovieFinder

ColonDelimitedMF

MovieLister

<<creates>>class MovieLister {

private MovieFinder finder;public MovieLister() {

finder = new ColonDelimitedMF("movies1.txt");}

public Movie[] moviesDirectedBy(String arg) {List allMovies = finder.findAll();for (Iterator it = allMovies.iterator(); it.hasNext();) {

Movie movie = (Movie) it.next();if (!movie.getDirector().equals(arg))

it.remove();}return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);

}}

13.11.2008Robert Walter ‐MSI 3 9

/ 46

Using an Interface right

<<interface>>MovieFinder

ColonDelimitedMF

MovieLister

class MovieLister {private MovieFinder finder;public MovieLister(MovieFinder inFinder) {

finder = inFinder;}

public Movie[] moviesDirectedBy(String arg) {List allMovies = finder.findAll();for (Iterator it = allMovies.iterator(); it.hasNext();) {

Movie movie = (Movie) it.next();if (!movie.getDirector().equals(arg))

it.remove();}return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);

}}

In a complex system, a configuration needs to know whichLister gets which implementation injected.

13.11.2008Robert Walter ‐MSI 3 10

/ 46

Overview

• Outline• References• Necessity• Simple Example Design• Summary

13.11.2008Robert Walter ‐MSI 3 11

/ 46

High Level Design ( Bob Lee)

ServiceClientClientTest

ServiceImpl• three approaches

– factory– dependency injection by Hand– dependency injection with Google Guice

• what differs?– how does client get an instance of service?

13.11.2008Robert Walter ‐MSI 3 12

/ 46

public interface Service {void go();

}

public class ServiceImpl implements Service {void go(){

// expensive stuff}

}

High Level Design ( Bob Lee)

Service

ServiceImpl

ClientClientTest

• what is constant?

13.11.2008Robert Walter ‐MSI 3 13

/ 46

High Level Design ( Bob Lee)

ServiceClientClientTest

MockServicepublic class MockService implements Service {private boolean gone = false;public void go(){

gone = true;}public boolean isGone(){

return gone;}

}

• what is constant?

13.11.2008Robert Walter ‐MSI 3 14

/ 46

Factory Approachpublic class Client {

public void go() {Service service = ServiceFactory.getInstance(); service.go();

}}

public class ServiceFactory {private ServiceFactory() {}

private static Service instance = new ServiceImpl();// decoupling clients from implementationpublic static Service getInstance() {return instance;

}// for testing purposepublic static void setInstance(Service service) {instance = service;

}}

13.11.2008Robert Walter ‐MSI 3 15

/ 46

Factory Approach – Unit Test

public void testClient() {

Service previous = ServiceFactory.getInstance();

try {final MockService mock = new MockService();ServiceFactory.setInstance(mock);Client client = new Client();client.go();assertTrue(mock.isGone());

}finally{// why is restoring necessary?ServiceFactory.setInstance(previous);

}}

– to avoid breaking other tests– tests in different threads could still break

13.11.2008Robert Walter ‐MSI 3 16

/ 46

Factory Observations

• Unit test has to pass the mock to the factory and then clean up afterwards.

• You have to look at the implementation of Client to know it depends on Service.

• Reusing Client in a different context will be difficult.

• Same factory code for every dependency• Client has a compile time dependency on 

ServiceImpl

13.11.2008Robert Walter ‐MSI 3 17

/ 46

Dependency Injection by Hand

public class Client {private final Service service;

public Client(Service service) {// Service service = ServiceFactory.getInstance();this.service = service;

}

public void go() {service.go();

}}

„Don`t call me. I`ll call you.“DI is a specific form of Inversion of Control

13.11.2008Robert Walter ‐MSI 3 18

/ 46

Factory – Unit Test (Again)

public void testClient() {

Service previous = ServiceFactory.getInstance();

try {final MockService mock = new MockService();ServiceFactory.setInstance(mock);Client client = new Client();client.go();assertTrue(mock.isGone());

}finally{ServiceFactory.setInstance(previous);

}}

13.11.2008Robert Walter ‐MSI 3 19

/ 46

public void testClient() {MockService mock = new MockService();Client client = new Client(mock);client.go();assertTrue(mock.isGone());

}

Dependency Injection – Unit Test

13.11.2008Robert Walter ‐MSI 3 20

/ 46

DI Observations

• Business objects are easy to test• Clients can`t be created without a Service• Client can be used with different services, even in 

the same application• No compile time dependency between Client and 

ServiceImpl– Dependency is moved to the application layer– Testing small compilation units possible

• How is a client created / configured?– Factory?

13.11.2008Robert Walter ‐MSI 3 21

/ 46

DI Client creation

public class ClientFactory {

private ClientFactory() {}

public static Client getInstance() {Service service = ServiceFactory.getInstance();return new Client(service);

}}

Even more factories needed on application layer!More general: DI configuration can be tough and annoying!

13.11.2008Robert Walter ‐MSI 3 22

/ 46

Dependency Injection with Guice

public class MyModule extends AbstractModule {

protected void configure() {bind(Service.class) // bind Service Interface.to(ServiceImpl.class) // to ServiceImpl.in(Scopes.SINGLETON); // and make it a Singleton

} }

Replacing factories by modules:

13.11.2008Robert Walter ‐MSI 3 23

/ 46

Dependency Injection with Guice

public class Client {private final Service service;

@Inject // tells Guice where to inject thingspublic Client(Service service) {this.service = service;

}

public void go() {service.go();

}}

Guice needs to know where it should inject something(instead of ourselfs implementing factories)

13.11.2008Robert Walter ‐MSI 3 24

/ 46

DI with Guice ‐ Testing

public void testClient() {MockService mock = new MockService();Client client = new Client(mock);client.go();assertTrue(mock.isGone());

}

Stays exactly the same!

13.11.2008Robert Walter ‐MSI 3 25

/ 46

Using Guice ‐ Bootstrapping

public class MyApplication {public static void main(String[] args) {Injector injector = Guice.createInjector(new MyModule());Client client = injector.getInstance(Client.class);client.go();

}}

public class MyModule extends AbstractModule {

protected void configure() {bind(Service.class) // bind Service Interface.to(ServiceImpl.class) // to ServiceImpl.in(Scopes.SINGLETON); // and make it a Singleton

} }

Injector only needs to be applied once. All other dependenciesget injected by Guice.

13.11.2008Robert Walter ‐MSI 3 26

/ 46

Adding further dependencies

public class ServiceImpl implements Service {@Inject // the new „new“Emailer emailer;

void go(){// expensive stuff...// send confirmationemailer.send(...);

}}

public class Emailer { // concrete class...send(...) { ... }

}

ServiceImpl is „in the club“ (thanks to the injector)

13.11.2008Robert Walter ‐MSI 3 27

/ 46

Overview

• Outline• References• Necessity• Simple Example Design• Summary

13.11.2008Robert Walter ‐MSI 3 28

/ 46

Summary

• DI is a simple yet powerful mechanism• Identification of where to use it is hard• Different frameworks support developers with

– „lightweight components“ / injectors– more fancy stuff

• Benefits– Decoupling of components– Testing / Reusability / Flexibility / Maintainability

• Downsides– Configuration needed (could get complex)