DI Framework: как избежать постоянной передачи внедренных зависимостей вверх по цепочке и без использования локатора сервисов (в частности, с Ninject)

Мне нужна дополнительная помощь, чтобы «понять», как фреймворк DI, такой как Ninject, выходит за рамки основ.

Возьмите образец Ninject:

class Samurai {
private IWeapon _weapon;

    [Inject]
    public Samurai(IWeapon weapon) {
      _weapon = weapon;
    }

    public void Attack(string target) {
      _weapon.Hit(target);
    }
 }

Без структуры DI (т.е. ссылок на [Inject] выше) ссылающийся класс выглядел бы примерно так:

class Program {
   public static void Main() { 
    Samurai warrior1 = new Samurai(new Shuriken());
    Samurai warrior2 = new Samurai(new Sword());
    warrior1.Attack("the evildoers");
    warrior2.Attack("the evildoers");
  }
}

... где вы все обновляете. Да, вы удалили зависимость в Samurai, но теперь у вас есть зависимость на один шаг дальше по цепочке. Простой.

С Ninject вы избавитесь от всего нового:

class Program {
  public static void Main() {
    IKernel kernel = new StandardKernel(new WarriorModule());
    Samurai warrior = kernel.Get<Samurai>();
    warrior.Attack("the evildoers");
  }
}

ОДНАКО, это моя область путаницы: без создания какого-либо средства поиска сервисов, чтобы позаботиться об эффективном обновлении применимого ядра и модуля (т.е. .IoC.TypeResolver.Get ‹> ()), что является а) лучшим способом не нужно обновлять ядра повсюду (повсюду ссылки на поисковики служб?), и б) что более важно, когда у вас есть большая длинная цепочка с зависимостями с собственными зависимостями, вы доводите ее до крайности, проходя инъекции на всем пути в чем-то, что, я уверен, является серьезным анти-шаблоном (друг назвал это анти-шаблоном «инъекции горячих зависимостей»).

Другими словами, я думал, что часть магии фреймворка DI заключается в том, что вам не нужно постоянно вставлять зависимости по всей цепочке (т.е. ваша первая ссылка, содержащая 10 параметров в своем конструкторе, ни один из которых не имеет ничего общего с что угодно, пока не будет намного дальше по цепочке) - где же магия или решение моей путаницы, так что зависимости не будут постоянно ссылаться вверх и вверх по цепочке, а ссылки на поисковики сервисов распространяются повсюду.

Дальнейшая путаница для меня заключается в том, что при использовании структуры DI, как лучше всего справиться со сценарием, когда указанному классу нужен IList, который обычно помещается в конструктор (то есть новый ReferencedClass (myList)) как это, кроме в простых случаях, таких как строка подключения к базе данных, не летает. Просто создайте свойство и установите его после создания / обнаружения службы DI Framework? Т.е.

var referencedClass = IoC.Get<IReferencedClass>();
referencedClass.MyList = myList;

В общем, я думаю, что это пост, который меня, вероятно, смущает, когда я его получу, но прямо сейчас я слишком много раз бился головой об стену, пытаясь определить лучший подход.


person Ted    schedule 02.01.2010    source источник


Ответы (1)


Что касается проблемы зависимости от «горячего картофеля», этого не должно происходить. Фреймворк внедрения зависимостей сделает это за вас.

Например, если Class1 имеет зависимость от Class2, а Class2 имеет зависимость от Class3, вам не нужно вставлять Class3 в Class1, чтобы учесть зависимость Class2. Ядро пройдется по цепочке зависимостей за вас и автоматически разрешит последующие зависимости (при условии, что все участвующие классы были зарегистрированы в ядре), когда вы запросите Class1.

Class1 зависит от Class2 зависит от Class3

В конструкторе Class1 вообще нет необходимости упоминать Class3.

Что касается второй проблемы, то как или если это будет сделано, зависит от структуры. Я думаю, что с Ninject вы можете использовать синтаксис Bind().To().WithConstructorArgument(), чтобы предоставить конструктору новый элемент списка.

person Eric King    schedule 03.01.2010
comment
1) Пощечина. Не знаю, как мои тесты раньше терпели неудачу, но, конечно же, без проблем справляется с подключением вещей вверх по цепочке. 2) Синтаксис WithConstructorArgument изменился в Ninject v2, но я не могу вспомнить его сразу. Тем не менее, для этого требуется, чтобы либо ваш статический шлюз / локатор службы обрабатывал эту настройку, либо вам нужно было ссылаться на ядро, когда вы хотите его установить, либо аргумент должен быть известен до времени выполнения (т.е. строка подключения к базе данных). Поэтому я просто устанавливаю свойство, которое кажется более хрупким, но оно работает и соответствует тому, что я читал раньше. - person Ted; 03.01.2010