@skosterin88

В чем преимущество Dependency Injection перед использованием оператора new?

Приветствую многоуважаемое сообщество Тостера.
Недавно открыл для себя Dependency Injection. Прочитал кучу статей на эту тему в инете, но так и не понял, в чем его прелесть. Решил разобраться на практическом примере. Допустим, есть у нас некое подобие игры, в которой мы имеем дело с юнитами-солдатами. У солдат есть оружие. Общее описание оружия содержится в интерфейсе IWeapon:
public interface IWeapon
    {
        int Ammo { get; set; }
        string Name { get; }
        void Fire();
    }


Общее описание солдата хранится в интерфейсе ISoldier:
public interface ISoldier
    {
        string Type { get; }
        IWeapon Weapon { get; set; }
        void Move();
    }


Есть две реализации интерфейса IWeapon: AssaultRifle и MachineGun. Соответственно, есть две реализации интерфейса ISoldier: Rifleman и MachineGunner. Задание оружия для них выполняется в их конструкторах следующим образом:
public class Rifleman : ISoldier
    {
        private IWeapon _weapon;
        public Rifleman()
        {
            _weapon = new AssaultRifle();
        }
        ...
    }

    public class MachineGunner : ISoldier
    {
        private IWeapon _weapon;
        public MachineGunner()
        {
            _weapon = new MachineGun();
        }
        ...
    }


Так создание экземпляров классов и их использование выглядит в функции Main():

//Классический случай
            ISoldier rifleman = new Rifleman();
            ISoldier machineGunner = new MachineGunner();

            for (int i = 0; i < 10; i++)
            {
                rifleman.Weapon.Fire();
                machineGunner.Weapon.Fire();
            }


Если же использовать Dependency Injection через тот же Ninject, то вот как мне придется это делать:

public class AssaultRifleConfigModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IWeapon>().To<AssaultRifle>();
        }
    }

    public class MachineGunConfigModule : NinjectModule
    {

        public override void Load()
        {
            Bind<IWeapon>().To<MachineGun>();
        }
    }
    public class RiflemanConfigModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISoldier>().To<Rifleman>();
        }
    }

    public class MachineGunnerConfigModule : NinjectModule
    {

        public override void Load()
        {
            Bind<ISoldier>().To<MachineGunner>();
        }
    }

    public class Rifleman : ISoldier
    {
        ...
        public Rifleman()
        {
            IKernel weaponKernel = new StandardKernel(new AssaultRifleConfigModule());
            _weapon = weaponKernel.Get<IWeapon>();
        }
        ...
    }
  
    public class MachineGunner : ISoldier
    {
        ...
        public MachineGunner()
        {
            IKernel weaponKernel = new StandardKernel(new MachineGunConfigModule());
            _weapon = weaponKernel.Get<IWeapon>();
        }
        ...
    }
    ...
    static void Main(string[] args)
    {
            ...
            //DI через NInject
            IKernel soldierKernel = new StandardKernel(new RiflemanConfigModule());
            ISoldier rifleman = soldierKernel.Get<ISoldier>();

            soldierKernel = new StandardKernel(new MachineGunnerConfigModule());
            ISoldier machineGunner = soldierKernel.Get<ISoldier>();

            for (int i = 0; i < 10; i++)
            {
                rifleman.Weapon.Fire();
                machineGunner.Weapon.Fire();
            }
    }

Вопрос: чем способ с применением Dependency Injection лучше использования оператора new в данной ситуации?
  • Вопрос задан
  • 1153 просмотра
Пригласить эксперта
Ответы на вопрос 2
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
но так и не понял, в чем его прелесть.


Не делать все руками. Описать граф зависимостей а он уже как-то сам соберется. То есть собирать граф зависимостей руками оно может даже правильнее, но с точки зрения читабельности кода, поддерживаемости кода (особенно учитывая миллионы зависимостей во фреймворках), да и просто увеличения скорости разработки, лучше использовать какой-нибудь контейнер в main и там запросить вершину графа зависимостей. А он уж разберется.

По сути главное что бы ваш код о контейнере ничего не знал. ЧТо бы вы в один долгий зимний вечер могли влегкую заменить все на "ручную" сборку и сам код трогать не пришлось... Ну аннотации/атрибуты это уже компромис для удобства.
Ответ написан
Комментировать
Ох.. такой фигни вы нагородили. Вызывать Get<> вручную не очень хорошо, все должно быть заинжекчено через конструктор или пропертя. Почитайте еще немного про Dependency Injection(DI), понять самому это возможно)). Дальше будет стена текста, но она описывает реальную(сверическую в вакуме) ситуацию.

Касательно примера. Тут глубина вложености не большая и DI будет только мешать. Но давайте представим, что у вас есть веб сервер, который ждет запроса и возвращает определеные данные. Он эти данные должен откудато взять, нам их любезно готов предоставить некий сервис по имени Service, который реализует некий интерфейс IService. Для корретной роботы Service, тоже необходимы данные, которые он преобразует и вернет тому, кто их попросит. Он их достает из SuperServiceHelper(ISuperServiceHelper) и SuperPuperServiceHelper(ISuperPuperServiceHelper). Первый хелпер берет данные из репозиториев SomeEntityRepository(ISomeEntityRepository) и SomeGreenEntityRepository(ISomeGreenEntityRepository). Второй хелпер берет данные из SomeBlueEntityRepository(ISomeBlueEntityRepository) и SomeRedEntityRepository(ISomeRedEntityRepository). Ну и конешно мы хочем все это валидировать нам нужны валидаторы для каждой entity, которые для SomeEntityRepository нужен SomeEntityValidator, для SomeGreenEntityRepository нужен SomeGreenEntityValidator, для SomeBlueEntityRepository нужен SomeBlueEntityValidator, для SomeRedEntityRepository нужен SomeRedEntityValidator.

И так, вы не забыли с чего мы начали, после всей этой ваханалии(еще нам нужно, чтобы все это было покрыто тестами)? Ах да, нам нужен Service для того чтобы обработать запрос... Нам бл*** нужен только Service, здесь и сейчас. Вот здесь DI и IoC нас спасут, скажите только что вам нужен IService и вуаля, вся эта цепочка будет востановлена без нашего участия.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы