DI and IoC for beginners

    The DI / IoC topic is quite simple, but it’s very difficult to find a good description of how it works and why you need it on the web. Here is my attempt using Unity. Whether the topic is well explained is up to you to judge.


    Let's create a simple example:

    // сервис. содержит бизнес-логику
    public class MyService

    {

      public MyService()

      {

        ⋮

      }

      public void DoSomething()

      {

        ⋮

      }

    }

     

    // окошко. пользуется бизнес-логикой
    public class MyWindow : Form

    {

      private MyService service;

      public MyWindow()

      {

        service = new MyService();

      }

    }

     

    // програмка которая показывает окошко
    public class MyProgram

    {

      static void Main()

      {

        ⋮

        /// создаем окно
        Application.Run(new MyWindow);

      }

    }

    There is one problem in this code: the class is MyWindowtoo attached to the class MyService. This is bad because

    • It will not work to write a unit test for MyWindowin isolation from MyService. In fact, we get an integration test.
    • It is impossible to replace MyServicewith OtherService, unless OtherServiceit inherits from it. If MyServiceseveral classes depend on you, you will have to change them all.
    • If our service requires configuration, it will have to be configured in each class that uses it.

    In short, our problem is this operator new(). In order to competently control dependencies and allow yourself to test objects in isolation, you need to refuse this operator.

    How? This is exactly what the IoC and DI patterns do. IoC (Inversion of Control) is a pattern in which the control of an object (in our case, the lifetime of an object) is assigned to some component. Some kind of outsourcing - instead of creating the object ourselves (through new()), we request it from the so-called IoC-container, that is, a factory that can competently produce objects.

    True, each time asking for a copy of the object from the container is lazy. In these cases, we can use a different pattern. DI (Dependency Injection)allows us to automatically pull the dependencies we need from the container during initialization. That is, when we create MyWindowthrough the IoC container, the DI mechanism can magically initialize MyServicewithout our direct participation.

    How it works?


    We will use the Unity framework for our program. To begin with, we rewrite it Main()- create a container in it and apply DI to our window:

    public class MyProgram

    {

      static void Main()

      {

        ⋮

        var uc = new UnityContainer();

        Application.Run(uc.Resolve());

      }

    }

    By the Resolve()container method , we request not only the creation of an object of the type MyWindow, but also the automatic creation of all its dependencies . Now let's look at how you can get the creation of a service (i.e. the dependent part) automatically. First, pull out the service interface so that you can change it later:

    interface IService

    {

      void DoSomething();

    }

     

    public class MyService : IService

    {

      ⋮ // все как и раньше
    }

    Now we change MyWindowso that the interface is used. There are several options for how you can add a link to a service so that the container initializes it. Here is one of them:

    public class MyWindow : Form

    {

      private IService service;

      public MyWindow(IService service)

      {

        this.service = service;

      }

    }

    Now it remains to do only one thing - to tell the container so that at the request of objects of the type IServiceit produces MyService:

    public class MyProgram

    {

      static void Main()

      {

        ⋮

        var uc = new UnityContainer();

        uc.RegisterType();

        Application.Run(uc.Resolve());

      }

    }

    That's all!!! Now, when the program starts, the variable servicewindow will be initialized automatically . This is called constructor injection. Want to leave the constructor empty? You are welcome:

    public class MyWindow : Form

    {

      [Dependency]

      public IService Service { get; set; }


     

      public MyWindow()

      {

        ⋮

      }

    }

    Changing the field to a property and marking it with an attribute [Dependency], we hinted to the container that it needs to be initialized when creating the class. The result is the same as with the constructor. This technique is called “setter injection”.

    That's all! Thanks for attention! If you are interested in the topic and you live in St. Petersburg, come to our meeting tomorrow .

    Read Part 2 of the series

    Also popular now: