Inversion of control

23
Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011 Dependency Injection and Inversion of Control Developing flexible, reusable and testable software Moslem Rashidi

Transcript of Inversion of control

Page 1: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Dependency Injection and Inversion of Control

Developing flexible, reusable and testable software

Moslem Rashidi

Page 2: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Something about the author

• This pattern was introduced in 2004 by Martin Fowler

• Martin Fowler is an author and international speaker on software development, specializing in object-oriented analysis and design, UML, patterns and agile software development.

• In March 2000, he became Chief Scientist at ThoughtWorks, a systems integration and consulting company.

Page 3: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Intent

Intent of Dependency injection pattern:

It refers to the process of supplying an external dependency to a software component

Page 4: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

What is a “Dependency”?

Some common dependencies include:

• Application Layers

– Data Access Layer & Databases

– Business Layer

• External services & Components

– Web Services

– Third Party Components

• Framework Components

– File Objects (File.Delete(…), Directory.Exists(…))

– Web Objects (HttpContext, Session, Request, etc)

Page 5: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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!

Page 6: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

A Concrete Example – A Trade Monitor

Page 7: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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();

Page 8: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Trade Monitor – The Design Refactored (1)

• Introduce interface/implementation separation

– Logic does not depend on DAO anymore.

– Does this really solve the problem?

public class TradeMonitor { private ILimitRepository limitRepository; public TradeMonitor() { limitRepository = new LimitDao(); } public bool TryTrade(string symbol, int amount) {

. . . } }

public interface ILimitRepository { int GetExposure(string symbol); int GetLimit(string symbol); }

limitRepository = new LimitDao();

• The constructor still has a static dependency on DAO

Page 9: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Trade Monitor – The Design Refactored (2)• Introduce Factory

• TradeMonitor decoupled from LimitDao

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

TradeMonitor

<<interface>>LimitRepository

LimitDao

<<creates>>

return new LimitDao();

• LimitDao still tightly-coupled albeit to Factory

Page 10: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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) { . . . }}

Page 11: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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?

Page 12: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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;} } }

• The dependencies are injected from the outside

• Components are passive and are not concerned with locating or creating dependencies

This is Setter Dependency Injection

Page 13: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Another Idea

• Why not just use the constructor?

public class TradeMonitor{ private ILimitRepository limitRepository; public TradeMonitor(ILimitRepository limitRepository) { this.limitRepository = limitRepository; }}

• 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

This is Constructor Dependency Injection

Page 14: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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.

Page 15: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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

Page 16: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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.

Page 17: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

IoC Containers and Features

Container Setter DI

Ctor DI

External config

Code config

Auto-wiring

Lifecycle support`

Url

System.ComponentModel a a Part of .Net framework

PicoContainer.Net a a a a a http://picocontainer.org

Windsor a a a ? a http://www.castleproject.org

StructureMap a a a P a http://sourceforge.net/projects/structuremap

Spring.Net a a a ? a a http://www.springframework.net/

ObjectBuilder a a a a ?? a http://msdn.microsoft.com

?? = More investigation

? = Setter based DI required for primitive dependencies

P = Partial still requires configuration to point to assemblies to scan

Page 18: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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); }}

Page 19: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

The Solution – Using the Windsor Container

• Code configurationIWindsorContainer 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 configurationIWindsorContainer 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>

Page 20: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

The Solution – Complex Configuration

• What if components take parameters?public class LimitDao{ public LimitDao(string connectionString) {…}}

public class TradeMonitor{ public TradeMonitor(string[] monitoredSymbols) {…}}

<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> . . .

<configuration> <components> <component id='tradeMonitor' type='AAABank.TradeMonitor, AAABank'> <monitoredSymbols> <array> <elem>MSFT</elem> <elem>TWUK</elem> </array> </monitoredSymbols>. . .

Page 21: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Many other possibilities

• Container creates objects – but what objects?

• Can return proxy – no need for MarshalByRef inheritance.

• Object instance caching.

• Aspect Oriented Programming (concerns ,advice ,join point, pointcut).

• Remoting by configuration.

• Automatic Web Service creation.

• . . .

Page 22: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

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

Page 23: Inversion of control

Amirkabir University of Technology (Tehran Polytechnic)- Fall 2011

Questions ?