मुख्य सामग्रीवर वगळा

Dependency Inversion Principle, Dependency Injection and Inversion Of Control

Dependency Inversion Principle, Dependency Injection and Inversion Of Control

I’m a big fan of Dependency Injection pattern since it allows me to create more testable, maintainable and loosely coupled components. While it is easy to apply this pattern if you’ve seen some code samples you need more time to understand principles and terms around this pattern. In this article I will try to illustrate differences between those terms and explain how they come to work together.
I will use well known car/engine example in this article but any example with dependency will do. All examples are written in C# language.
Let’s first define few example classes:
public class Engine {}

public class Car
{
    private Engine _engine;

    public Car()
    {
        _engine = new Engine();
    }
}
n the example above we can see that Car (higher level component) depends on Engine (lower level component). At some point we may need to make car more reusable, for example to be able to work with different engines. It’s obvious that example above isn’t good enough since our Car class creates concrete Engine inside its constructor. How can we tackle that problem? By applying Dependency Inversion Principle (DIP).

Dependency Inversion Principle

DIP principle simply states that higher level modules should not depend on lower level modules but both should depend on abstractions. Higher level module should also own this abstraction effectively now making lower level module depend on that abstraction and thus depend on higher level module.
Example:
public interface IEngine {}

public class Engine: IEngine {}

public class Car
{
    private IEngine _engine;
}
In this example we can see that our Car now depends on abstraction rather then concrete Engine type, also concrete Engine implements our abstraction which must be packaged with Car to satisfy DIP principle. Another way of saying this would be that abstraction should represent Car needs not Engine possibilities.
In this example I omitted Engine creation part on purpose since DIP principle doesn’t state exactly how dependencies should be created. Actually we can create them in few different ways, by using Dependency injection (DI), Service location or Factory pattern.
And so we came to DI pattern which is the way to facilitate the run-time provisioning of the chosen low-level component implementation to the high-level component.

Dependency Injection pattern

DI pattern deals with the place where dependencies are created. In DI pattern dependency creation is not responsibility of dependent class but rather of its creator.
In the simplest form dependencies are injected through class constructor or by setting class properties.
Example:
public interface IEngine {}

public class Engine: IEngine {}

public class Car
{
    private IEngine _engine;

    public Car(IEngine engine)
    {
        _engine = engine;
    }
}
In this example, we can see that concrete Engine type is being injected into Car class through constructor. Car class is not any more responsible of Engine creation meaning that concrete Engine implementation can be injected at runtime.
Advantages of using this pattern are following:
  1. It promotes loosely coupling of components.
  2. It makes components testable because at test time we can mock out everything that is not part of our test.
  3. It allows runtime resolution of dependencies (configuration).
Only one more thing left and that is the way we create our dependent class. We can simply instantiate all dependencies and inject them to dependent class at the time of creation.
var engine = new Engine();
var car = new Car(engine);
As we can see this method includes boilerplate code in every place where we need to create our dependent class (Car). It gets even worse if dependency (Engine) has dependencies of its own and so on.
We have applied nice pattern with a lot of advantages and now we want to further improve it by removing boilerplate code. To facilitate DI we can use Inversion of control container.

Inversion of control

oC container is responsible of object creation providing all necessary dependencies on the way. To do this it must know about all abstractions and all concrete types to use for that abstractions. To work with IoC container we must configure it first.
In the following example I will use Microsoft Unity container syntax.
Configuration:
var container = new UnityContainer()
    .RegisterType<IEngine, Engine>();
With the code above we have configured container to resolve IEngine abstraction with concrete Engine class. We could also configure container using configuration files making it possible to change how application works without compilation.
Resolution:
var car = container.Resolve<Car>();
Resolve method above will create our Car object and it will automatically supply correct Engine in its constructor.
Complete example:
public interface IEngine {}

public class Engine: IEngine {}

public class Car
{
    private IEngine _engine;

    public Car(IEngine engine)
    {
        _engine = engine;
    }
}

var container = new UnityContainer()
    .RegisterType<IEngine, Engine>();

var car = container.Resolve<Car>();
Our goal is complete. Now we can use our car with different engines. Also now we have loosely coupled components easy to test and maintain.

टिप्पण्या

या ब्लॉगवरील लोकप्रिय पोस्ट

WCF Windows communication foundation

WCF:Why WCF Actually, WCF is going to unify all in one : web services : enterprise service : Remoting What is WCF? WCF  stands for  Windows Communication Foundation  and is part of .NET 3.0. WCF is Microsoft platform for building distributed and interoperable applications. What is a distributed application? In simple terms a distributed application, is an application where parts of it run on 2 or more computers.  Distributed applications  are also called as  connected systems. Examples: A web application running on one machine and a web service that this application is consuming is running on another machine.     An enterprise web application may have the following tiers, and each tier may be running on a different machine 1. Presentation tier 2. Business tier   3. Data Access tier     Why build distributed applications? There are several reasons for this 1.  An enterprise application ma...
Enacpsulation in C# Encapsulation is defined as the wrapping up of data under a single unit. It is the mechanism that binds together code and the data it manipulates. In a different way, encapsulation is a protective shield that prevents the data from being accessed by the code outside this shield. Technically in encapsulation, the variables or data of a class are hidden from any other class and can be accessed only through any member function of own class in which they are declared. As in encapsulation, the data in a class is hidden from other classes, so it is also known as  data-hiding . Encapsulation can be achieved by:  Declaring all the variables in the class as private and using  C# Properties  in the class to set and get the values of variables. // C# program to illustrate encapsulation using System; public class DemoEncap {  // private variables declared  // these can only be accessed by  // public methods of class  ...

Create WCf service using 2 end points

1.  Creating a WCF service 2.  Hosting the WCF service using a console application 3.  Exposing 2 service endpoints.  4.  Creating a windows and a web Client applications.   Let's take the scenario that we discussed in  Part 2 .  We have 2 clients and we need to implement a service a for them.  1.  The first client is using a Java application to interact with our service, so for interoperability this client wants meesages to be in XML format and the protocl to be HTTP. 2.  The second client uses .NET, so for better performance this client wants messages formmated in binary over TCP protocol. In  Part 2 , To meet the requirement of the first client, we implemented a  web service  and to meet the requirement of the second client we implemented a  remoting service. In this video, we will create a single  WCF service,  and  configure 2 endpoints  to meet the requirements of both the clients. Crea...