1 / 19

Dependency Injection and Inversion of Control

Dependency Injection and Inversion of Control. Developing flexible, reusable and testable software Part 1 Nick Hines March 2006. Loosely Coupled Systems. Good OO Systems – organised as web of interacting objects Goal – High cohesion, low coupling. Advantages of low coupling Extensibility

presley
Télécharger la présentation

Dependency Injection and Inversion of Control

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Dependency Injection and Inversion of Control Developing flexible, reusable and testable software Part 1 Nick Hines March 2006

  2. Loosely Coupled Systems • Good OO Systems – organised as web of interacting objects • Goal – High cohesion, low coupling • Advantages of low coupling • Extensibility • Testability • Reusability • Not so easy to achieve!

  3. A Concrete Example – A Trade Monitor

  4. Trade Monitor – The design • TradeMonitor is coupled to LimitDao – this is not good! • Extensibility – what if not database but distributed cache • Testability – where do the limits for test come from? • Reusability – logic is fairly generic . . . public class TradeMonitor { private LimitDao limitDao; public TradeMonitor() { limitDao = new LimitDao(); } public bool TryTrade(string symbol, int amount) { int limit = limitDao.GetLimit(symbol); int exposure = limitDao.GetExposure(symbol); return (exposure + amount > limit) ? false : true; } } public class LimitDao { public int GetExposure(string symbol) { // Do something with the database } public int GetLimit(string sysmbol) { // Do something with the database } } limitDao = new LimitDao();

  5. Trade Monitor – The Design Refactored (1) • Introduce interface/implementation separation • Logic does not depend on DAO anymore. • Does this really solve the problem? • The constructor still has a static dependency on DAO public interface ILimitRepository { int GetExposure(string symbol); int GetLimit(string symbol); } public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor() { limitRepository = new LimitDao(); } public bool TryTrade(string symbol, int amount) { . . . } } limitRepository = new LimitDao();

  6. Trade Monitor – The Design Refactored (2) • Introduce Factory • TradeMonitor decoupled from LimitDao • LimitDao still tightly-coupled albeit to Factory public class LimitFactory { public static ILimitRepository GetLimitRepository() { return new LimitDao(); } } public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor() { limitRepository = LimitFactory.GetLimitRepository(); } public bool TryTrade(string symbol, int amount) { . . . } } LimitFactory return new LimitDao(); <<creates>> TradeMonitor LimitDao <<interface>> LimitRepository

  7. Trade Monitor – The Design Refactored (3) • Introduce ServiceLocator • This gives us extensibility, testability, reusability public class ServiceLocator { public static void RegisterService(Type type, object impl) {. . .} public static object GetService(Type type) {. . .} } public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor() { object o = ServiceLocator.GetService(typeof(ILimitRepository)); limitRepository = o as ILimitRepository; } public bool TryTrade(string symbol, int amount) { . . . } }

  8. ServiceLocator - Problems • Sequence dependence • Cumbersome setup in tests • Service depends on infrastructure code, (ServiceLocator) • Code needs to handle lookup problems • Aren’t these problem minor? Why settle for something we know has issues?

  9. A Different View • What about adding a setter and let something else worry about creation and resolution? public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor() { } public ILimitRepository Limits { set { limitRepository = value;} } } This is SetterDependency Injection • The dependencies are injected from the outside • Components are passive and are not concerned with locating or creating dependencies

  10. Another Idea • Why not just use the constructor? public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor(ILimitRepository limitRepository) { this.limitRepository = limitRepository; } } This is ConstructorDependency Injection • No setters for dependent components, (obviously) • One-shot initialisation – components are always initialised correctly • All dependencies are clearly visible from code • It is impossible to create cyclic dependencies

  11. What about Inversion of Control? • Dependency Injection - one example of IoC design principle. • Also known as the Hollywood Principle • Don’t call us, we’ll call you! • Objects rely on their environment to provide dependencies rather than actively obtaining them. • Inversion of Control can make the difference between a library and a framework.

  12. IoC Containers • There are still some open questions • Who creates the dependencies? • What if we need some initialisation code that must be run after dependencies have been set? • What happens when we don’t have all the components? • IoC Containers solve these issues • Have configuration – often external • Create objects • Ensure all dependencies are satisfied • Provide lifecycle support

  13. It Gets Better • We can use reflection to determine dependencies – no need for config files. • Most IoC containers support auto-wiring. • Make components known to container. • Container examines constructors and determines dependencies. • Auto-wiring provides other benefits. • Less typing, especially long assembly names. • Static type checking by IDE at edit time. • More intuitive for developer.

  14. IoC Containers and Features

  15. The Solution – Test Case [TestFixture] public class TradeMonitorTest { [Test] public void MonitorBlocksTradesWhenLimitExceeded() { DynamicMock mockRepository = new DynamicMock(typeof(ILimitRepository)); mockRepository.SetupResult('GetLimit', 1000000, new Type[] { typeof(string) }); mockRepository.SetupResult('GetExposure', 999999, new Type[] { typeof(string) }); TradeMonitor monitor = new TradeMonitor((ILimitRepository)mockRepository.MockInstance); Assert.IsFalse(monitor.TryTrade('MSFT', 1000), 'Monitor should block trade'); } } public class TradeMonitor { private ILimitRepository repository; public TradeMonitor(ILimitRepository repository){this.repository = repository; } public bool TryTrade(string symbol, int amount) { int limit = repository.GetLimit(symbol); int exposure = repository.GetExposure(symbol); return ((amount + exposure) <= limit); } }

  16. The Solution – Using the Windsor Container • Code configuration IWindsorContainer container = new WindsorContainer(); container.AddComponent('limitRepository', typeof(ILimitRepository), typeof(LimitDao)); container.AddComponent('tradeMonitor', typeof(TradeMonitor)); TradeMonitor monitor = (TradeMonitor)container['tradeMonitor']; monitor.TryTrade('MSFT', 1000); • External configuration IWindsorContainer container = new WindsorContainer('config.xml'); TradeMonitor monitor = (TradeMonitor)container['tradeMonitor']; monitor.TryTrade('MSFT', 1000); <configuration> <components> <component id='limitRepository' service='AAABank.ILimitRepository, AAABank' type='AAABank.LimitDao, AAABank' /> <component id='tradeMonitor' type='AAABank.TradeMonitor, AAABank' /> </components> </configuration>

  17. The Solution – Complex Configuration • What if components take parameters? public class LimitDao { public LimitDao(string connectionString) {…} } <configuration> <components> <component id='limitRepository' service='AAABank.ILimitRepository, AAABank' type='AAABank.LimitDao, AAABank'> <connectionString>Data Source=AServer;Initial Catalog=BankDB;User ID=sa</connectionString> </component> . . . public class TradeMonitor { public TradeMonitor(string[] monitoredSymbols) {…} } <configuration> <components> <component id='tradeMonitor' type='AAABank.TradeMonitor, AAABank'> <monitoredSymbols> <array> <elem>MSFT</elem> <elem>TWUK</elem> </array> </monitoredSymbols> . . .

  18. Many other possibilities • Container creates objects – but what objects? • Can return proxy – no need for MarshalByRef inheritance. • Object instance caching. • Aspect Oriented Programming. • Remoting by configuration. • Automatic Web Service creation. • . . .

  19. Summary • Container based DI facilitates: - • Testability • Extensibility • Reusability • Makes the difference between framework and library • Not just use but extend • Essential for complex Domain Driven Design • Easier to separate 'infrastructure' from business logic

More Related