150 likes | 211 Vues
Phil Pratt-Szeliga CSE 784 Fall 2010. Dependency Injection. References. Dependency Injection Design patterns using Spring and Guice Dhanji R. Prasanna http://en.wikipedia.org/wiki/Dependency_injection. Hardwired Dependencies. public interface SpellChecker {
E N D
Phil Pratt-Szeliga CSE 784 Fall 2010 Dependency Injection
References • Dependency Injection Design patterns using Spring and Guice Dhanji R. Prasanna • http://en.wikipedia.org/wiki/Dependency_injection
Hardwired Dependencies public interface SpellChecker { bool checkSpelling(Document doc); } public class EnglishSpellChecker : SpellChecker { public bool checkSpelling(Document doc){ //check the spelling here } } public class Emailer { private SpellChecker mSpellChecker; private Document mDocument; public Emailer( ){ mSpellChecker = new EnglishSpellChecker(); } public bool send( ) { if(mSpellChecker.checkSpelling(mDocument)) { return true; } else { return false; } } }
Problems with Hardwired Dependencies • Development • What if your application needs to suddenly support Spanish spell checkers, etc • Testing • How can you pass in a mock SpellChecker to test that checkSpelling has been called?
Pre-DI Solutions • Construction by hand • Factory Pattern
Construction by Hand public class Emailer {private SpellChecker mSpellChecker; private Document mDocument; public Emailer(SpellChecker checker){ mSpellChecker = checker } public bool send( ) { if(mSpellChecker.checkSpelling(mDocument)) { return true; } else { return false; } } } Emailer emailer1 = new Emailer(new EnglishSpellChecker()); Emailer emailer2 = new Emailer(new SpanishSpellChecker());
Construction by Hand • Advantages: • Useful for small projects. Provides a powerful way of separating assemblies in small projects • Disadvantages • Clients may directly depend on English and Spanish spell checker.
Separating Assemblies • The problem: A high precision metal melting factory uses a C# program to control the furnace temperature. • The control program is one assembly • The recipe editor is another assembly that can run in standalone mode but has dependencies on the control program • The control program has an Furnace class with a huge number of dependencies (~10,000 SLOC) • We want to isolate the RecipeEditor class from the Furnace class so the Metallurgist can use the RecipeEditor at his/her desk
Separating Assemblies • The solution namespace Furnace { public class Furnace { public showRecipeEditor( ) { new RecipeEditor(new OnlineRecipeEditorToFurnaceBridge( )).show(); } } private class OnlineRecipeEditorToFurnaceBridge : RecipeEditorToFurnaceBridge { public int getElapsedTime( ) { return Furnace.getElapsedTime(); } } } namespace RecipeEditor { public interface RecipeEditorToFurnaceBridge ( ) { int getElapsedTime( ); } private class OfflineRecipeEditorToFurnaceBridge : RecipeEditorToFurnaceBridge { public int getElapsedTime( ) { return 0; } } public class RecipeEditor { private RecipeEditorToFurnaceBridge mBridge; public RecipeEditor( RecipeEditorToFurnaceBridge bridge) { mBridge = bridge; } } }
Factory Pattern public class EmailerFactory { public Emailer newEnglishEmailer( ) { return new Emailer(new EnglishSpellChecker); } public Emailer newSpanishEmailer( ) { return new Emailer(new SpanishSpellChecker); } } Emailer emailer = new EmailerFactory( ).newEnglishEmailer( );
Factory Pattern • Advantages • The clients are completely isolated from EnglishSpellChecker and SpanishSpellChecker • Disadvantages • Testing with Mock objects is hard to get right • With many dependencies there is a lot of boilerplate code written manually • When you want to change a common dependency you have to change it many places
Dependency Injection (Spring.NET) IApplicationContext ctx = ContextRegistry.GetContext(); Emailer english_emailer = (Emailer)ctx.GetObject("EnglishEmailer"); Emailer spanish_emailer = (Emailer)ctx.GetObject("SpanishEmailer"); Document english_document = new Document("hello world"); Document spanish_document = new Document("hola mundo"); english_emailer.send(english_document); spanish_emailer.send(spanish_document); - ContextRegistry is configured with App.config file
Dependency Injection Scope Singleton – For each context and identifier, GetObject returns the same object Prototype – Each time GetObject is called for a specific identifier, a new object is returned