dependency injection - hochschule konstanz · 13.11.2008 robert walter ‐msi 3 5 / 46 references...
TRANSCRIPT
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)