/*
Author: Jiangong SUN
*/
Last update : 11/09/2013
What is Inversion of Control ?
The object of IoC is to eliminate the code coupling.
The ideas of IoC are:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
What is dependency Injection ?
DI is a form of IoC, where implementations are passed into an object through constructors/setters/service look-ups, which the object will 'depend' on in order to behave correctly.
Do not instantiate the dependencies explicitly in your class. Instead, declaratively express dependencies in your class definition. Use a Builder object to obtain valid instances of your object's dependencies and pass them to your object during the object's creation and/or initialization.
Typically, you express dependencies on interfaces instead of concrete classes. This enables easy replacement of the dependency concrete implementation without modifying your classes' source code.
There are some ways of implementing Dependency Injection.
- Constructor Injection
- Setter Injection
- Interface based Injection
- Service locator Injection
- Generic Injection
In constructor injection, you use parameters of the object's constructor method to express dependencies and to have the builder inject it with its dependencies.
In setter injection, the dependencies are expressed through setter properties that the builder uses to pass the dependencies to it during object initialization.
Dependency Injection Tools ?
Spring.NET, Ninject, Windsor
Here I will create a code sample who is tightly coupled.
public class Customer
{
private Address _address;
public Customer()
{
_address = new Address();
}
public void GetAddress()
{
Console.WriteLine("{0}, {1}, {2}, {3}", _address.Name, _address.Road, _address.City, _address.Country);
}
}
public class Address
{
public string Name { get; set; }
public string Road { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
If "Address" class has different subclasses who inherit from it, and we need to call them in Customer class, we have to add instance of different subclasses and potentially modify "GetAddress" method.
public class FacturationAddress : Address
{
public string FacturationName { get; set; }
public void GetAddress()
{
Console.WriteLine("FacturationName: {0}, {1}, {2}, {3}, {4}", FacturationName, Name, Road, City, Country);
}
}
public class DeliveryAddress : Address
{
public string DeliveryName { get; set; }
public void GetAddress()
{
Console.WriteLine("DeliveryName: {0}, {1}, {2}, {3}, {4}", DeliveryName, Name, Road, City, Country);
}
}
With the above code, I think I should totally rewrite the "Customer" class. For example, I can add instances of "FacturationAddress" and "DeliveryAddress" to meet the requirement. But if there will be more subclasses in the future, I have to re-modify the "Customer" class which is not recommended.
As Inversion of Control indicates, the higher level modules should not depend on lower high modules, and they need to depend on abstractions. And abstractions should not depend details, details should depend on abstractions.
This means, "Customer" and "Address" should depend on abstractions, and the implementations of Addresses' methods should be transparent to the "Customers".
Firstly, I will introduce to you an example of Constructor Injection to solve the problem.
I will create an interface who declares some properties and method, the classes who implement it will declare the implementation of declared method .
public interface IAddress
{
string Name { get; set; }
string Road { get; set; }
string City { get; set; }
string Country { get; set; }
string GetAddress();
}
public class FacturationAddress : IAddress
{
public string FacturationName { get; set; }
public string Name { get; set; }
public string Road { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string GetAddress()
{
return string.Format("Facturation Address: {0},{1},{2},{3},{4}", FacturationName, Name, Road, City, Country);
}
}
public class DeliveryAddress : IAddress
{
public string DeliveryName { get; set; }
public string Name { get; set; }
public string Road { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string GetAddress()
{
return string.Format("Delivery Address: {0},{1},{2},{3},{4}", DeliveryName, Name, Road, City, Country);
}
}
Then in Customer class, create a local Address interface variable and assign an object in class constructor. And then, use a method to call the local variable's method.
public class Customer
{
private IAddress _address;
public Customer(IAddress address)
{
_address = address;
}
public string GetAddress()
{
return _address.GetAddress();
}
}
To test with fake data, we can see that it works perfectly. And when I need to add new classes, I don't need to modify Customer class.
FacturationAddress facturationAddress = new FacturationAddress()
{
FacturationName = "Charles sun",
City = "paris",
Country = "France",
Name = "charles",
Road = "haussman"
};
Customer customer = new Customer(facturationAddress);
Console.WriteLine(customer.GetAddress());
DeliveryAddress deliveryAddress = new DeliveryAddress()
{
DeliveryName = "jiangong",
City = "Paris",
Country = "France",
Name = "charles",
Road = "12 avenue champs élysée"
};
Customer customer2 = new Customer(deliveryAddress);
Console.WriteLine(customer2.GetAddress());
But there is a limitation which is I need to pass an object to the class constructor, which may cause problems.
Then I will present Setter Injection to solve the same problem.
I will create the same IAddress interface, FacturationAddress and DeliveryAddress as the previous solution.
But the Customer class will change.
public class Customer
{
private IAddress _address { get; set; }
public IAddress Address
{
get { return _address; }
set { _address = value; }
}
public string GetAddress()
{
return _address.GetAddress();
}
}
In this version, I don't need to pass an object in the class constructor. I just need to instanciate the Address instance of IAddress.
Customer customer = new Customer();
FacturationAddress facturationAddress = new FacturationAddress()
{
FacturationName = "Charles sun",
City = "paris",
Country = "France",
Name = "charles",
Road = "haussman"
};
customer.Address = facturationAddress; //setter
Console.WriteLine(customer.GetAddress());
DeliveryAddress deliveryAddress = new DeliveryAddress()
{
DeliveryName = "jiangong",
City = "Paris",
Country = "France",
Name = "charles",
Road = "12 avenue champs élysée"
};
customer.Address = deliveryAddress; //setter
Console.WriteLine(customer.GetAddress());
In this way, the problem is solved too.
Service Locator is another way to implement Inversion of Control.
I will present how to solve the same problem with Service Locator.
public class ServiceLocator
{
//public ServiceLocator() { }
private IAddress _Address;
public IAddress GetService(IAddress address)
{
if (_Address == null)
{
_Address = address;
}
return _Address;
}
}
With Customer class, it just need to call the method of service locator.
public class Customer
{
private IAddress _address { get; set; }
public Customer(IAddress address)
{
_address = new ServiceLocator().GetService(address);
}
public string GetAddress()
{
return _address.GetAddress();
}
}
In this way, in case of addtion of new classes, Customer class doesn't need to be modified.
FacturationAddress facturationAddress = new FacturationAddress()
{
FacturationName = "Charles sun",
City = "paris",
Country = "France",
Name = "charles",
Road = "haussman"
};
Customer customer = new Customer(facturationAddress);
Console.WriteLine(customer.GetAddress());
DeliveryAddress deliveryAddress = new DeliveryAddress()
{
DeliveryName = "jiangong",
City = "Paris",
Country = "France",
Name = "charles",
Road = "12 avenue champs élysée"
};
Customer customer2 = new Customer(deliveryAddress);
Console.WriteLine(customer2.GetAddress());
Reference:
http://www.codeproject.com/Articles/29271/Design-pattern-Inversion-of-control-and-Dependency
http://www.codeproject.com/Articles/380748/Inversion-of-Control-Overview-with-Examples
http://www.codeproject.com/Articles/592372/Dependency-Injection-DI-vs-Inversion-of-Control-IO
http://www.codeproject.com/Articles/465173/Dependency-Inversion-Principle-IoC-Container-and-D
http://www.codeproject.com/Articles/31210/Model-View-Presenter
http://msdn.microsoft.com/en-us/library/ff921087(v=pandp.20).aspx
http://www.codeproject.com/Articles/13831/Dependency-Injection-for-Loose-Coupling
http://stackoverflow.com/questions/6550700/inversion-of-control-vs-dependency-injection
http://www.dotnet-tricks.com/Tutorial/dependencyinjection/bSVa100413-Understanding-Inversion-of-Control,-Dependency-Injection-and-Service-Locator.html
http://stackoverflow.com/questions/1710005/abstractions-should-not-depend-upon-details-details-should-depend-upon-abstract
http://www.springframework.net/doc-latest/reference/html/introduction.html#introduction-overview