Introduction
The main purpose of the article is to give some help to those who want to learn something about the inversion of control design pattern.
I've decided to create the article because I've spent a lot of time trying to find something easy to read and useful on the internet about this pattern.
The article is based on a simple and complete application example.
First of all you should know that the main purpose of the inversion of controls design pattern is to avoid dependencies between classes. Using inversion of controls your code gets
decoupled so you can easily exchange implementations of an interface with alternative implementations.
Using the code
Bellow you can see the structure of my (dummy) project:
What is the project about?
My projects is just reading some data (a list of products) and displays the data on the console.
The data may come from the SQL database or from XML file.
So, I have the object Product that has the next properties:
public class Product
{
public Guid ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public double Quantity { get; set; }
}
So, the main secret of my project that implements the inversion of control pattern are the next classes:
IProductDeposit
interface, Container
,
and ProductDepositListeners
class.
IProductDeposit
interface has just a simple method: GetProducts()
.
public interface IProductsDeposit
{
List<Product> GetProducts();
}
Than, I have ProductDepositDB
and ProductDepositXML
classes which are both implementing the
IProductDeposit
interface.
ProductDepositDB
class is used to get my list of products from my SQL database and
ProductDepositXML
is used to get my data from an XML file (bellow you can see a snippet of code from my XML file)
="1.0"="utf-8"
<products>
<product>
<id>9a8f19e0-1abe-401b-a28f-ae0978538cd1</id>
<name>Milli</name>
<description>Milk</description>
<price>2</price>
<quantity>50</quantity>
</product>
<product>
<id>9a8f19e0-1abe-401b-a28f-ae0978538cd2</id>
<name>Fulga</name>
<description>Milk</description>
<price>3</price>
<quantity>60</quantity>
</product>
</products>
ProductDepositListeners
is the class where the magic happens. This is where the injection of control take place. The class has an
IProductsDeposit
private field which is responsible for the injection process.
namespace InversionOfControlsDummyApp.AppModels
{
public class ProductsDepositListener
{
private IProductsDeposit _productsDeposit;
public ProductsDepositListener(IProductsDeposit productsDeposit)
{
_productsDeposit = productsDeposit;
}
public List<Product> GetProducts()
{
return _productsDeposit.GetProducts();
}
}
}
Now, it's very easy to choose how do I want to get my products (from my XML file or from my database) without changing anything in the source code.
The Container
class makes the decision.
public class Container
{
public delegate object ChooseDatasource();
private readonly Dictionary<Type, ChooseDatasource> _datasources = new Dictionary<Type, ChooseDatasource>();
public void AddComponent<T>(ChooseDatasource ds)
{
_datasources.Add(typeof(T), ds);
}
public T Create<T>()
{
return (T)_datasources[typeof(T)]();
}
}
In my Main
class I just need to create an instance of the
Container
object where I will add a component of
ProductsDepositListener
class and a component from which I want to get my data (ProductDepositDB
or
ProductDepositXML
).
This is the the Main
class when I want to get my list of products from my database:
static void Main(string[] args)
{
Container container = new Container();
container.AddComponent<ProductsDepositListener>(() =>
{
IProductsDeposit fileReader = container.Create<IProductsDeposit>();
return new ProductsDepositListener(fileReader);
});
container.AddComponent<IProductsDeposit>(() =>
{
IProductsDeposit pdDB = new ProductsDepositDB();
return pdDB;
});
ProductsDepositListener productDepositListener = container.Create<ProductsDepositListener>();
var result = productDepositListener.GetProducts();
DisplayProducts(result);
This is the the Main
class when I want to get my list of products from my XML file:
static void Main(string[] args)
{
Container container = new Container();
container.AddComponent<ProductsDepositListener>(() =>
{
IProductsDeposit fileReader = container.Create<IProductsDeposit>();
return new ProductsDepositListener(fileReader);
});
container.AddComponent<IProductsDeposit>(() =>
{
IProductsDeposit pdXML = new ProductDepositXML();
return pdXML;
});
ProductsDepositListener productDepositListener = container.Create<ProductsDepositListener>();
var result = productDepositListener.GetProducts();
DisplayProducts(result);
I hope that this article will be a good starting point for those who want to implement the inversion of control pattern in their applications.
I have also added a zip with my application for those who needs it.