Dependency Injection

1,242 views

Published on

A draft of the DI talk. I think that concepts are Ok but the format must be changed. In any case the content is the most important here and that it is ok.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,242
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
31
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Dependency Injection

  1. 1. Dependency Injection
  2. 2. Practice, Practice, Practice <ul><li>A young concert violinist got lost on his way to a performance. He stoped an old man on the corner and asked him how to get to Carnige Hall. The old man looked at the violinist and the violin tucked under his arm, and said: “ Practice, son, Practice ! ” </li></ul><ul><li>Theory-Practice: a positive feedback system </li></ul>
  3. 3. Why DI?
  4. 4. <ul><li>public class Emailer { </li></ul><ul><ul><li>private SpellChecker spellChecker; </li></ul></ul><ul><ul><li>public Emailer() { </li></ul></ul><ul><ul><ul><li>this.spellChecker = new SpellChecker(); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public void send(String text) { .. } </li></ul></ul><ul><li>} </li></ul><ul><li>How about testing? </li></ul><ul><li>public class MockSpellChecker extends SpellChecker { </li></ul><ul><ul><li>private boolean didCheckSpelling = false; </li></ul></ul><ul><ul><li>public boolean checkSpelling(String text) { </li></ul></ul><ul><ul><ul><li>didCheckSpelling = true; </li></ul></ul></ul><ul><ul><ul><li>return true; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public boolean verifyDidCheckSpelling(){ </li></ul></ul><ul><ul><ul><li>return didCheckSpelling; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>And what about the Open-Closed Principle? </li></ul>
  5. 5. The Factory Pattern <ul><li>public class EmailerFactory { </li></ul><ul><ul><li>/* The Factory is explicit about the object it produces...*/ </li></ul></ul><ul><ul><li>public Emailer newItalianEmailer() { </li></ul></ul><ul><ul><ul><li>return new Emailer(new ItalianSpellChecker()); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>Emailer service = new EmailerFactory().newItalianEmailer(); </li></ul><ul><ul><li>@Test </li></ul></ul><ul><ul><li>public void testEmailer() { </li></ul></ul><ul><ul><ul><li>MockSpellChecker spellChecker = ... </li></ul></ul></ul><ul><ul><ul><li>Emailer emailer = new Emailer( spellChecker ); </li></ul></ul></ul><ul><ul><ul><li>emailer.send(&quot;Fatte DI in ufficio!&quot;); </li></ul></ul></ul><ul><ul><ul><li>assert ...; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul>Client Factory Service
  6. 6. What about the client? public class EmailerClient { private Emailer emailer = new EmailerFactory().newItalianEmailer(); public void sendEmail() { emailer.send(createMessage()); } } public class EmailerFactory { private static Emailer instance; // did you see the problem here? public Emailer newEmailer() { if (null == instance) return new Emailer(..); return instance; } static void set (Emailer mock) { instance = mock; } } @Test public void testEmailClient() { MockEmailer mock = new MockEmailer(); EmailerFactory.set(mock); new EmailClient().sendEmail(); assert mock.correctlySent(); } @Test public void testEmailClient() { MockEmailer mock = new MockEmailer(); EmailerFactory.set(mock); try { new EmailClient().sendEmail(); assert mock.correctlySent(); } finally { EmailerFactory.set(null); } }
  7. 7. The Service Locator Pattern <ul><li>Is a generalization of the Factory </li></ul><ul><li>Emailer emailer = (Emailer) new ServiceLocator().get(&quot;ItalianEmailer&quot;); </li></ul><ul><li>Emailer emailer = (Emailer) new ServiceLocator().get(&quot; JapaneseEmailerWithPhoneAndEmail &quot;); </li></ul><ul><li>(as a Factory) suffers from same problems: testability and shared state </li></ul><ul><li>Do you remember the ServiceManager anti-pattern? </li></ul>
  8. 8. DI <ul><li>Hollywood Principle </li></ul><ul><li>Behaviorally focused </li></ul><ul><li>Modular </li></ul><ul><li>Separation of concerns, Demeter's law, Decoupling </li></ul><ul><li>TESTABLE </li></ul>
  9. 9. My main(String... args) is better than yours... <ul><li>public class EmailerClient { </li></ul><ul><ul><li>private final Emailer emailer; </li></ul></ul><ul><ul><li>public EmailerClient(Emailer emailer){ </li></ul></ul><ul><ul><ul><li>this.emailer = emailer; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public void sendEmail() { </li></ul></ul><ul><ul><ul><li>emailer.send(createMessage()); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>... </li></ul></ul><ul><li>} </li></ul><ul><li>public static void main(String... args) throws Exception { // by hand </li></ul><ul><li>(new ApplicationFactory(args).create()).start(); </li></ul><ul><li>} </li></ul><ul><li>public static void main(String... args) throws Exception { // using Guice </li></ul><ul><ul><li>Injector injector = Guice.createInjector(new ApplicationModule(args)); </li></ul></ul><ul><li>(injector.getInstance(Application.class)).start(); </li></ul><ul><li>} </li></ul>
  10. 10. What have changed? <ul><li>Client code (EmailerClient) doesn't invoke the injector (separation of concerns) </li></ul><ul><li>The root-object is explicit (Application.class) </li></ul><ul><li>The injector obtains instances within current execution context (scoping) </li></ul>
  11. 11. How to identify a dependency <ul><li>Dependency: </li></ul><ul><ul><li>Contract </li></ul></ul><ul><ul><li>Variants </li></ul></ul><ul><ul><li>public interface SpellChecker { </li></ul></ul><ul><ul><ul><li>boolean check(String text); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>class ItalianSpellChecker implements SpellChecker { </li></ul></ul><ul><ul><ul><li>public boolean check(String text) {/* check italian */ ...} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>class EnglishSpellChecker implements SpellChecker { </li></ul></ul><ul><ul><ul><li>public boolean check(String text) {/* check english */ ...} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>(new Emailer( new EnglishSpellChecker() )).send(“Hello”); </li></ul></ul><ul><ul><li>(new Emailer( new ItalianSpellChecker() )).send(“Ciao”); </li></ul></ul><ul><li>KEY: </li></ul><ul><ul><li>Unique </li></ul></ul><ul><ul><li>Arbitrary </li></ul></ul><ul><ul><li>Explicit </li></ul></ul>
  12. 12. String Keys (Spring) <ul><li>Pros </li></ul><ul><ul><li>Unique </li></ul></ul><ul><ul><li>Arbitrary </li></ul></ul><ul><ul><li>Explicit </li></ul></ul><ul><li>Cons </li></ul><ul><ul><li>Error-prone </li></ul></ul><ul><ul><li>No compile-time safe </li></ul></ul><ul><ul><li>You “can't” use Vim but IntelliJ Idea </li></ul></ul><ul><ul><li>Must be carefully choosen </li></ul></ul><ul><li>Examples: </li></ul><ul><li><beans ...> </li></ul><ul><li><bean id=”spelling.english” class=”it.xpug.milan.service.text. EnglishSpellChecker ”/> </li></ul><ul><li><bean id=”spelling.italian” class=”it.xpug.milan.service.text.ItalianSpellChecker”/> </li></ul><ul><li></beans> </li></ul>
  13. 13. Identifying by Type <ul><li>Pros </li></ul><ul><ul><li>Compile-time safe </li></ul></ul><ul><ul><li>Right direction </li></ul></ul><ul><li>Cons </li></ul><ul><ul><li>Not unique </li></ul></ul><ul><ul><li>Not arbitrary </li></ul></ul><ul><ul><li>Explicitness? </li></ul></ul><ul><ul><li>Inflexible </li></ul></ul><ul><li>Examples: </li></ul><ul><li>SpellChecker.class identifies EnglishSpellChecker or ItalianSpellChecker </li></ul><ul><li>Emailer.class identifies itself </li></ul>
  14. 14. Combinatorial Keys (Guice) <ul><li>Key = [contract, variant] </li></ul><ul><ul><li>Key1: [Emailer.class, “english”] </li></ul></ul><ul><ul><li>Key2: [Emailer.class, “italian”] </li></ul></ul><ul><ul><li>Key3: [Emailer.class, “English”] </li></ul></ul><ul><li>Or better: </li></ul><ul><ul><li>Key1: [Emailer.class, English.class] </li></ul></ul><ul><ul><li>Key2: [Emailer.class, Italian.class] </li></ul></ul><ul><ul><li>@Retention( RUNTIME ) </li></ul></ul><ul><ul><li>@Target( { PARAMETER } ) </li></ul></ul><ul><ul><li>public @interface English { </li></ul></ul><ul><ul><li>... </li></ul></ul><ul><ul><li>} </li></ul></ul>public class Emailer{ private final SpellChecker spellChecker; @Inject public SpellCheckerClient( @English SpellChecker spellChecker) { this. spellChecker = spellChecker; } } public class SpellingModule extends AbstractModule { @Override protected void configure() { bind(SpellChecker.class) . annotatedWith(English.class) . to(EnglishSpellChecker.class); }
  15. 15. Injection Idioms <ul><li>Setter injection </li></ul><ul><li>public class Emailer { </li></ul><ul><ul><li>private SpellChecker spellChecker; </li></ul></ul><ul><ul><li>public void setSpellChecker (SpellChecker spellChecker) { </li></ul></ul><ul><ul><ul><li>this. spellChecker = spellChecker; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li><beans ...xmlns:p=&quot;http://www.springframework.org/schema/p&quot;...> </li></ul><ul><ul><li><bean id=&quot;spelling.english&quot; class=&quot;package.EnglishSpellChecker&quot;/> </li></ul></ul><ul><ul><li><bean id=”emailer” class =”package.Emailer” p:spellChecker=&quot;spelling.english&quot;/> </li></ul></ul><ul><li></beans> </li></ul><ul><li>Interface Injection (Deprecated) </li></ul><ul><li>Method Decoration (via AOP) </li></ul>
  16. 16. Constructor Injection <ul><li>public class Emailer { </li></ul><ul><li>private final SpellChecker spellChecker; // potentially immutable </li></ul><ul><ul><li>public Emailer(SpellChecker spellChecker){ </li></ul></ul><ul><ul><ul><li>this.spellChecker = spellChecker; // freezing the object graph </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li><beans ...> </li></ul><ul><ul><li><bean id=”emailer” class =”package.Emailer”> </li></ul></ul><ul><ul><ul><li><constructor-arg> </li></ul></ul></ul><ul><ul><ul><li><bean class=&quot;package.EnglishSpellChecker&quot;/> </li></ul></ul></ul><ul><ul><ul><li></constructor-arg> </li></ul></ul></ul><ul><ul><li></bean> </li></ul></ul><ul><li></beans> </li></ul>
  17. 17. Constructor vs Setter Injection <ul><li>Constructor </li></ul><ul><ul><li>Clear contract </li></ul></ul><ul><ul><li>Immutability support or temporal encapsulation </li></ul></ul><ul><ul><li>Ready for use/ Object validity </li></ul></ul><ul><ul><li>No need for noisy setters </li></ul></ul><ul><li>Setter </li></ul><ul><ul><li>Esplicitness (type indipendent) </li></ul></ul><ul><ul><li>Doesn't suffers from the pyramid/telescoping problem </li></ul></ul><ul><ul><li>Doesn't suffers from the circular dependency and some in-construction problems </li></ul></ul>
  18. 18. GOF's Creational Patterns <ul><li>Abstract Factory </li></ul><ul><li>Factory Method </li></ul><ul><li>Builder </li></ul><ul><li>Prototype </li></ul><ul><li>Singleton </li></ul>
  19. 19. The Reinjection Problem <ul><li>When a long-lived dependent needs a short-lived dependencies </li></ul><ul><li>public class Granny { </li></ul><ul><ul><li>private Apple apple; </li></ul></ul><ul><ul><li>public Granny(Apple apple) { </li></ul></ul><ul><ul><ul><li>this.apple = apple; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public void eat() { </li></ul></ul><ul><ul><ul><li>apple.consume(); </li></ul></ul></ul><ul><ul><ul><li>apple.consume(); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>/** Provides specific object and scoped intances of those objects. */ public interface Provider<T> { T get(); } public class AppleProvider implements Provider<Apple>{ public Apple get(){ return new Apple(); } } public class Granny { private Provider<Apple> appleProvider; public Granny(Provider<Apple> ap) { this.appleProvider = ap; } public void eat() { appleProvider.get().consume(); appleProvider.get().consume(); } }
  20. 20. Or using the builder pattern.. public class Granny { private AppleBuilder builder; public Granny(AppleBuilder b) { this.builder = b; } public void eat() { builder.buildRedApple().consume(); builder.buildGreenApple().consume(); } } public class AppleBuilder { public Apple buildRedApple() { return new RedApple(); } public Apple buildGreenApple() { return new GreenApple(); } } and its Jochua's version...
  21. 21. The Contextual Injection Problem <ul><li>public class NewsletterManager { </li></ul><ul><ul><li>private final List<Recipient> recipients; </li></ul></ul><ul><ul><li>private final AssistedProvider<Deliverer, Newsletter> deliverer; </li></ul></ul><ul><ul><li>public NewsletterManager(List<Recipient> rs,AssistedProvider<Deliverer,Newsletter> dp){ </li></ul></ul><ul><ul><ul><li>this.recipients = rs; </li></ul></ul></ul><ul><ul><ul><li>this.deliverer = dp; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public void send(Newsletter letter) { </li></ul></ul><ul><ul><ul><li>for (Recipient recipient : recipients) { </li></ul></ul></ul><ul><ul><ul><ul><li>Deliverer d = deliverer.get(letter); </li></ul></ul></ul></ul><ul><ul><ul><ul><li>d.deliverTo(recipient); </li></ul></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>public interface AssistedProvider<T, C> { </li></ul><ul><ul><li>T get(C context); </li></ul></ul><ul><li>} </li></ul><ul><li>public class DelivererProvider implements AssistedProvider<Deliverer,Newsletter> { </li></ul><ul><ul><li>public Deliverer get(Newsletter letter) { </li></ul></ul><ul><ul><ul><li>return new Deliverer(letter); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  22. 22. Scope <ul><li>Is a fixed duration of time or method calls in which an object exists </li></ul><ul><li>A context under which a given key refers to the same instance </li></ul><ul><li>Mostly used: Singleton and No-Scope </li></ul><ul><li>Interesting scopes: Transaction, Web, Cache, Thread, Grid, Custom... </li></ul>
  23. 23. Why Scope? <ul><li>Applies the Hollywood Principle to the state of the objects </li></ul><ul><li>The injector manages the latent state of your objects </li></ul><ul><li>The injector ensures that the services get new instance of dependencies as needed </li></ul><ul><li>Implicity separates state by context </li></ul><ul><li>Reduces the necessity for state-aware application logic (separation of concerns – infrastructure/business logic) </li></ul>
  24. 24. Defining a Custom Scope <ul><li>Guice </li></ul><ul><li>public interface Scope { </li></ul><ul><ul><li><T> Provider<T> scope(Key<T> key, Provider<T> unscoped); </li></ul></ul><ul><li>} </li></ul><ul><li>public class TransactionScope implements Scope { </li></ul><ul><ul><li>private final ThreadLocal<Map<Key<?>, Object>> instances </li></ul></ul><ul><ul><ul><li>= new ThreadLocal<Map<Key<?>, Object>>(); </li></ul></ul></ul><ul><ul><li>public <T> Provider<T> scope( </li></ul></ul><ul><ul><ul><li>final Key<T> key, final Provider<T> unscoped) { </li></ul></ul></ul><ul><ul><ul><ul><li>return new Provider<T>() { </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>public T get() { </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>Map<Key<?>, Object> map = instances.get(); </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>if (null == map) { </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>throw new OutOfScopeException(&quot;no transaction was active&quot;); </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>} </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>if (!map.containsKey(key)) { </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><ul><li>map.put(key, unscoped.get()) </li></ul></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>} </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li>return (T) map.get(key); </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>} </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><li>}; </li></ul></ul></ul></ul><ul><li>} </li></ul><ul><li>// called by the transaction framework </li></ul><ul><li>public void beginScope() { instances.set(new HashMap<Key<?>, Object>()); } </li></ul><ul><li>public void endScope() {instances.remove();}} </li></ul>
  25. 25. Defining a Custom Scope (Cont.) <ul><li>Spring </li></ul><ul><li>public interface Scope { </li></ul><ul><ul><li>Object get(String key, ObjectFactory unscopedProvider); </li></ul></ul><ul><ul><li>Object remove(String key); </li></ul></ul><ul><ul><li>String getConversationId(); // scope identity, ex. SessionId </li></ul></ul><ul><ul><li>// lifecycle method </li></ul></ul><ul><ul><li>void registerDestructionCallback(String key, Runnable destructionCallback); </li></ul></ul><ul><li>} </li></ul><ul><li><beans ...> </li></ul><ul><ul><li><bean class=&quot;org.springframework.beans.factory.config.CustomScopeConfigurer&quot;> </li></ul></ul><ul><ul><ul><li><property name=&quot;scopes&quot;> </li></ul></ul></ul><ul><ul><ul><ul><li><map> </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li><entry key=&quot;transaction&quot;> </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><ul><li><bean class=&quot;my.custom.TransactionScope&quot;/> </li></ul></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li></entry> </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><li></map> </li></ul></ul></ul></ul><ul><ul><ul><li></property> </li></ul></ul></ul><ul><ul><li></bean> </li></ul></ul><ul><ul><li>... </li></ul></ul><ul><li></beans> </li></ul>
  26. 26. public class TransactionScope implements Scope { private final ThreadLocal<Map<String, Object>> instances = new ThreadLocal<Map<String, Object>>(); public Object get(String key, ObjectFactory unscoped) { Map<String, Object> map = instances.get(); //check out of scope if (null == map) throw new IllegalStateException(&quot;no transaction is active&quot;); if (!map.containsKey(key)) { map.put(key, unscoped.getObject()); } return map.get(key); } public void beginScope() { instances.set(new HashMap<String, Object>()); } public void endScope() { instances.remove(); } public Object remove(String key) { // check out of scope ... return instances.get().remove(key); } public String getConversationId() { // check out of scope ... return instances.get().toString(); } public void registerDestructionCallback(String key, Runnable destructionCallback) { ... } }
  27. 27. public static final Scope SINGLETON = new Scope() { public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { return new Provider<T>() { /* * The lazily initialized singleton instance. Once set, this will either have type T or will * be equal to NULL. */ private volatile Object instance; // DCL on a volatile is safe as of Java 5, which we obviously require. @SuppressWarnings(&quot;DoubleCheckedLocking&quot;) public T get() { if (instance == null) { /* * Use a pretty coarse lock. We don't want to run into deadlocks * when two threads try to load circularly-dependent objects. * Maybe one of these days we will identify independent graphs of * objects and offer to load them in parallel. */ synchronized (InjectorBuilder.class) { if (instance == null) { T nullableInstance = creator.get(); instance = (nullableInstance != null) ? nullableInstance : NULL; } } } Object localInstance = instance; // This is safe because instance has type T or is equal to NULL @SuppressWarnings(&quot;unchecked&quot;) T returnedInstance = (localInstance != NULL) ? (T) localInstance : null; return returnedInstance; } public String toString() { return String.format(&quot;%s[%s]&quot;, creator, SINGLETON); } }; } }; Did you see how it helps to the modularity?
  28. 28. Related stuff <ul><li>Lifecycle </li></ul><ul><li>Postprocessors </li></ul><ul><li>AOP </li></ul>
  29. 29. Lifecycle in DI <ul><li>Closely related to the scope but of different semantic </li></ul><ul><li>Related to the nature of the object </li></ul><ul><li>Is about state transitions / event driven </li></ul><ul><li>Moslty used: init and destroy </li></ul><ul><li>Can be constructed over other specific lifecycles (ex. servlet or db connections) </li></ul><ul><li>PostProcessors – register to a particular lifecycle </li></ul>
  30. 30. AOP in DI <ul><li>Attention: much power means much responsability! </li></ul><ul><li>Unit Testing somewhat is fading, much more like Integration Test </li></ul><ul><li>Aop Alliance (Guice, Spring) </li></ul><ul><li>AspectJ (Spring) </li></ul>
  31. 31. DI + AOP = DDD? <ul><li>Eric Evans - 2008 </li></ul><ul><li>Although DDD can be done with any paradigm that allows abstraction and expression of model concepts, in practice it has almost always been OOP. So, OOP has been a foundation of the practice of DDD. </li></ul><ul><li>More recently, DI has become a very widespread technique and, when used well, has helped practitioners of DDD to improve their designs. Most importantly, in my view, DI helps in the &quot;isolation&quot; of the domain. </li></ul><ul><li>The potential of AOP is just beginning to be realized. It seems that it has potential to declutter the domain layer. This contributes to the clear expression of the model concepts, and perhaps to isolation. But, unlike the first two (OOP and DI), it isn't a tried and true part of a &quot;foundation&quot; yet. </li></ul><ul><li>These form a foundation of a current best practice architecture that supports DDD . </li></ul>
  32. 32. DI for Java SE JSR 330 (299-Web Beans?) <ul><li>@Inject - Identifies injectable constructors, methods, and fields </li></ul><ul><li>@Qualifier - Identifies qualifier annotations </li></ul><ul><li>@Scope - Identifies scope annotations </li></ul><ul><li>@Named - String-based qualifier </li></ul><ul><li>@Singleton - Identifies a type that the injector only instantiates once </li></ul>
  33. 33. Some DI Frameworks <ul><li>Java </li></ul><ul><ul><li>APACHE AVALON </li></ul></ul><ul><ul><li>SPRING FRAMEWORK </li></ul></ul><ul><ul><li>PICOCONTAINER AND NANOCONTAINER </li></ul></ul><ul><ul><li>APACHE HIVEMIND </li></ul></ul><ul><ul><li>GOOGLE GUICE </li></ul></ul><ul><ul><li>BUTTERFLY DI DSL </li></ul></ul><ul><li>Ruby </li></ul><ul><ul><li>COPLAND </li></ul></ul>
  34. 34. References <ul><li>Dependency Injection - Dhanji R. Prasanna* </li></ul><ul><ul><li>http://www.manning.com/prasanna/ </li></ul></ul><ul><li>Misko Hevery* Blog </li></ul><ul><ul><li>http://misko.hevery.com/ </li></ul></ul><ul><li>* Many thanks to both of you from the slide's author </li></ul>

×