Внедрить разные классы, которые реализуют один и тот же интерфейс, используя Ninject - PullRequest
11 голосов
/ 21 декабря 2011

Я реализую шаблон конструктора для построения различных типов графических объектов, отображаемых в пользовательском интерфейсе WPF.Я использую Ninject в качестве контейнера IOC.Тем не менее, я пытаюсь найти элегантное расширяемое решение.

У меня есть ChartDirector объект, который принимает IChartBuilder в качестве зависимости.У меня также есть TemperatureChartBuilder и ThresholdChartBuilder, которые реализуют IChartBuilder.Я хочу ввести от TemperatureChartBuilder ИЛИ ThresholdChartBuilder до ChartDirector в зависимости от того, какое событие было запущено, или в зависимости от вызова клиента.Я проиллюстрировал мою проблему ниже в коде.

// ChartDirector also depends on this
kernel.Bind<IExample>().To<Example>();

// when called in Method X...
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>();

// when called in Method Y...
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder();

// TemperatureChartBuilder is a dependency of ChartDirector, need a way to dynamically
// allocate which binding to use.
var director = kernel.Get<ChartDirector>();

// without Ninject I would do
var director = new ChartDirector(new TemperatureChartBuilder);

// or
var director = new ChartDirector(new ThresholdChartBuilder);

РЕДАКТИРОВАТЬ:

В сочетании с ответом Гэри и отмечая небольшое изменение, что ChartDirector имеет другую зависимость, я теперь хочу сделать что-то вроде этого:

var director = kernel.Get<ChartDirector>().WithConstructorArgument(kernel.Get<IChartBuilder>("TemperatureChart"));

Возможно ли что-то подобное?

Ответы [ 2 ]

15 голосов
/ 21 декабря 2011

Если вы просто планируете использовать расположение службы, как в ваших примерах, то именованные привязки работают нормально, согласно ответу Garys.

Тем не менее, лучше использовать инжектор конструктора и использовать атрибуты.Например, из недействительной вики:

Bind<IWeapon>().To<Shuriken>().Named("Strong");
Bind<IWeapon>().To<Dagger>().Named("Weak"); 

...

class WeakAttack {
    readonly IWeapon _weapon;
    public([Named("Weak")] IWeapon weakWeapon)
        _weapon = weakWeapon;
    }
    public void Attack(string victim){
        Console.WriteLine(_weapon.Hit(victim));
    }
}

Исходя из вашего комментария к Гэри, вы (как ни странно) наткнулись на территорию, похожую на ту, что язадал вопрос несколько часов назад.См. Ответ Ремо здесь: Использование WithConstructorArgument и создание связанного типа

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

9 голосов
/ 21 декабря 2011

Я бы предложил использовать контекстные привязки (конкретно названные привязки) для достижения этой цели.Таким образом, вы можете сделать что-то вроде:

// called on app init
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>().Named("TempChartBuilder");   
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder().Named("ThreshChartBuilder");

// method X/Y could both call method Z that grabs the correct chart director
var director = new ChartDirector(kernel.Get<IChartBuilder>("TempChartBuilder"));

Где «TempChartBuilder» может быть переменной, которая сообщает ninject, какую привязку разрешить.Поэтому скорее связывание на лету, вы решите на лету, но все связывание может быть определено заранее.Обычно контейнеры IOC хранятся на уровне домена приложения и должны быть определены только один раз.В некоторых случаях может потребоваться динамическое связывание, но они должны быть редкими.

Подробнее о контекстных привязках: https://github.com/ninject/ninject/wiki/Contextual-Binding

...