WHAT’S GOING TO BE COVERED
• Quick intro to C# for Java developers
• Dependency Injection
• Inversion of Control
• Containers
• Castle Windsor
C# FOR JAVA DEVELOPERS
• Most of this lecture is going to use C#
• DI is possible with any object-oriented language
• IoC requires a container
C# FOR JAVA DEVELOPERS
Java C#public class Car{
private Engine engine;private string registration;private int windowCount;
public Car(){
this.engine = new Engine();this.registration = "SW14ABC";this.windowCount = 6;
}}
public class Car {private Engine engine;private String registration;private int windowCount;
public Car() {this.engine = new Engine();this.registration = "SW14ABC";this.windowCount = 6;
}}
C# FOR JAVA DEVELOPERS
Java C#private Engine engine;private string registration;
public Engine Engine{
get{
return this.engine;}set{
this.engine = value;}
}
public String Registration{
get{
return this.registration;}
}
private Engine engine;private String registration;
public Engine getEngine() {return this.engine;
}
public void setEngine(Engine engine) {this.engine = engine;
}
public String getRegistration() {return this.registration;
}
C# FOR JAVA DEVELOPERS
Java C#private string registration;
public Engine Engine { get; set; }
public String Registration{
get{
return this.registration;}
}
private Engine engine;private String registration;
public Engine getEngine() {return this.engine;
}
public void setEngine(Engine engine) {this.engine = engine;
}
public String getRegistration() {return this.registration;
}
C# FOR JAVA DEVELOPERS
Java C#public Engine Engine { get; set; }
public String Registration { get; private set; }
private Engine engine;private String registration;
public Engine getEngine() {return this.engine;
}
public void setEngine(Engine engine) {this.engine = engine;
}
public String getRegistration() {return this.registration;
}
C# FOR JAVA DEVELOPERS
Java C#using System;
namespace DITest{ public class Engine { public void GoFaster() { Console.WriteLine("VROOM!!"); } }}
import java.lang.*;
package com.stwalkerster.uni.masterclass;
public class Engine {public void goFaster() {
System.out.println("VROOM!!");}
}
”
“"Dependency Injection" is a 25-dollar term for a 5-cent concept.
James Shore - http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
LET’S LOOK AT AN EXAMPLE…
public class Car{
private Engine engine;
public Car(){
this.engine = new Engine();}
}
• The car makes a new engine when the car is being constructed
LET’S LOOK AT AN EXAMPLE…
public class Car{
private Engine engine;
public Car(){
this.engine = new Engine();}
}
• We move the creation of the dependency into whatever creates the object in the first place
LET’S LOOK AT AN EXAMPLE…
public class Car{
private Engine engine;
public Car(Engine engine){
this.engine = engine;}
}
• We move the creation of the dependency into whatever creates the object in the first place
• The created engine is now slotted into the car when the car is being constructed
BUT WHY?
• Better separation of code
• Better testability through stubs and mock objects
• The object doesn’t need to know implementation details of it’s dependency.
HIDING THE IMPLEMENTATION DETAILS
public class Car{
private Engine engine;
public Car(Engine engine){
this.engine = engine;}
}
• At the moment, our class depends on an instance of an Engine
• This Engine class is fully functioning
• This could be something like a database connection.
• But what if we wanted to swap this for an ElectricEngine? What about a DieselEngine?
public class ElectricCar{
private ElectricEngine engine;
public ElectricCar(ElectricEngine engine){
this.engine = engine;}
}
public class DieselCar{
private DieselEngine engine;
public DieselCar(DieselEngine engine){
this.engine = engine;}
}
INTERFACES
public class Car{
private IEngine engine;
public Car(IEngine engine){
this.engine = engine;}
}
• Now our class doesn’t care what sort of engine it gets, as long as it looks like and behaves like an engine, it’s good enough.
• As our IEngine interface exposes everything we need to access on the engine, we can continue as normal
INTERFACES
public class Car : ICar{
private IEngine engine;
public Car(IEngine engine){
this.engine = engine;}
}
• Now our class doesn’t care what sort of engine it gets, as long as it looks like and behaves like an engine, it’s good enough.
• As our IEngine interface exposes everything we need to access on the engine, we can continue as normal
UNIT TESTING
public class Car{
public Car(IEngine engine){
this.Engine = engine;}
public IEngine Engine { get; set; }
public void Drive(){
this.Engine.GoFaster();}
}
• If we want to test our Car class, we need an IEngine
• Unit testing is supposed to be of a small unit of code
• We can’t unit test the Drive() method…
… unless we have an engine which has no side effects!
• We use a mock or a stubbed-out implementation of the engine which does nothing.
• We simply pass this mock in with DI
UNIT TESTING
public class Car{
public Car(IEngine engine){
this.Engine = engine;}
public IEngine Engine { get; set; }
public void Drive(){
this.Engine.GoFaster();}
}
[Test]public void TestDriveCar(){
IEngine engine = new MockEngine();
Car myCar = new Car(engine);
// Check the Drive method does the// correct thing…
myCar.Drive()}
A QUICK NOTE…
• Dependencies don’t have to be injected through the constructor.
• It’s acceptable to pass in dependencies through properties or getter/setter methods too
INVERSION OF CONTROL
public class Application
{
public static void Main(string[] args)
{
IEngine engine = new ElectricEngine();
Car car = new Car(engine);
car.Drive();
}
}
• Dependency Injection removes the need for building dependencies from the class which depends on them
• The problem is just shifted to the caller.
• We need to know the concrete implementation of the Engine at compile time
INVERSION OF CONTROL
public class Application
{
public static void Main(string[] args)
{
IEngine engine = new ElectricEngine();
Car car = new Car(engine);
car.Drive();
}
}
• With Inversion of Control, we delegate the management of the entire lifecycle of the objects to a container.
INVERSION OF CONTROL
• The Inversion of Control framework (or container) is aware of the programmer’s code, and makes calls to it.
• This is the opposite of an API (which the developer calls). Hence “Inversion”
IOC CONTAINERS
• Castle Windsor (.NET)
• Spring (Java)
• Guice ( Java)
• Autofac (.NET)
• Ninject (.NET) Prism (.NET)
• StructureMap (.NET)
THREE CALLS
• The container is used in three different stages
• Bootstrapping
• Resolving
• Disposing
BOOTSTRAPPING
IWindsorContainer container =
new WindsorContainer()
.Install( FromAssembly.This() );
• Set up the IoC container, including:
• Creation of the container
• Configuration of the container
• Register all the components that are needed
RESOLVING
IApplication app =
container.Resolve<IApplication>();
• Get an instance of the root component of the application.
• This should be called as few times as possible – the vast majority of applications will have a single root component.
DISPOSING
container.Dispose();
• Often forgotten
• Shutting down the container will shutdown all the components it manages
• Remember the container manages the entire life cycle of components.
INSTALLERS
public void Install(IWindsorContainer container, IConfigurationStore store)
{container.Register(
Component.For<IEngine>()
.ImplementedBy<Engine>());
}
• Castle Windsor requires the use of an object which extends from IWindsorInstaller to register all of it’s components
• Registration can be split amongst WindsorInstallers
• During the bootstrapping phase, FromAssembly.This() loads all the installers into Castle Windsor
• Registration can be done one component at a time…
INSTALLERS
public void Install(IWindsorContainer container, IConfigurationStore store)
{container.Register(
Classes.FromThisAssembly().InNamespace(“DITest").WithService.AllInterfaces()
);}
• … or by convention.
• Registering by convention is preferred, as there is much less configuration to do.
• Castle also supports registering via XML files, which allows you to swap out classes at runtime, rather than at compile-time.