Я хотел бы присоединиться к использованию шаблона стратегии и DI.Как использовать шаблон стратегии вместе с инъекцией зависимостей (autofac)
Как использовать здесь Разрешение DI для получения соответствующего класса вместо if/else? Спасибо
Я хотел бы присоединиться к использованию шаблона стратегии и DI.Как использовать шаблон стратегии вместе с инъекцией зависимостей (autofac)
Как использовать здесь Разрешение DI для получения соответствующего класса вместо if/else? Спасибо
Вы можете использовать поиск по ключевым словам (Autofac Docs) и создать простую фабрику, которая разрешает правильный тип из ключа enum.
Сначала сконфигурируйте контейнер Autofac. Обратите внимание, что классы, основанные на IBase, привязаны к значениям перечисления. Завод зарегистрирован так, что вводимые в него значения ...
public class AutofacConfig
{
private static IContainer _container;
public static IContainer Container
{
get { return _container; }
}
public static void IoCConfiguration()
{
var builder = new ContainerBuilder();
builder.RegisterType<A>().Keyed<IBase>(MyEnum.A);
builder.RegisterType<B>().Keyed<IBase>(MyEnum.B);
builder.RegisterType<SomeFactory>();
_container = builder.Build();
}
}
Завод выглядит так. Обратите внимание, что IIndex впрыскивается на основе классов, созданные в качестве ключа к перечислению в конфиге ...
class SomeFactory
{
public IIndex<MyEnum, IBase> Classes { get; private set; }
public SomeFactory(IIndex<MyEnum, IBase> classes)
{
Classes = classes;
}
}
Context (сделанный SetClass общественность, так что код будет делать что-то) ...
public class Context
{
private IBase _usedClass;
public void SetClass(IBase usedClass)
{
_usedClass = usedClass;
}
public void Do()
{
_usedClass.Do();
}
}
чтобы увидеть его в действии ...
class Program
{
static void Main(string[] args)
{
AutofacConfig.IoCConfiguration();
using (var scope = AutofacConfig.Container.BeginLifetimeScope())
{
var factory = scope.Resolve<SomeFactory>();
var someEnum = GetEnum();
var someClass = factory.Classes[someEnum];
var context = new Context();
context.SetClass(someClass);
context.Do();
}
}
private static MyEnum GetEnum()
{
if (DateTime.Now.Millisecond%2 == 0)
{
return MyEnum.A;
}
return MyEnum.B;
}
}
Вы должны использовать инсталляция конструктора для вашего интерфейса IBase в конструкторе Context. Используя метод setter, вы игнорируете принцип инверсии управления, поскольку он сам обеспечивает зависимость вместо контейнера IoC. –
Если вы заметили, что на заводе используется конструктор. Контекст - это не тот вопрос, о котором идет речь. – dbugger
Я бы определенно использовать Delegate Factories, чтобы избежать зависимости от самого IoC контейнера. Используя Keyed Service Lookup, ваш код/фабрики будут тесно связаны с Autofac.
Вот хороший и чистый пример, без какой-либо зависимости от Autofac:
Стратегия:
public interface IStrategy { void Do(); }
public class ConcreteStrategyA : IStrategy { public void Do() { Console.WriteLine("Called ConcreteStrategyA.Do()"); } };
public class ConcreteStrategyB : IStrategy { public void Do() { Console.WriteLine("Called ConcreteStrategyB.Do()"); } };
Перечисления вы хотите включить:
public enum ESomeEnum
{
UseStrategyA,
UseStrategyB,
}
контекст, который потребляет стратегии:
private readonly Func<ESomeEnum, IStrategy> _strategyFactory;
public Context(Func<ESomeEnum, IStrategy> strategyFactory)
{
_strategyFactory = strategyFactory;
}
public void DoSomething()
{
_strategyFactory(ESomeEnum.UseStrategyB).Do();
_strategyFactory(ESomeEnum.UseStrategyA).Do();
}
И, наконец, конфигурация контейнера:
var builder = new ContainerBuilder();
builder.RegisterType<Context>().AsSelf().SingleInstance();
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(IStrategy)))
.Where(t => typeof(IStrategy).IsAssignableFrom(t))
.AsSelf();
builder.Register<Func<ESomeEnum, IStrategy>>(c =>
{
var cc = c.Resolve<IComponentContext>();
return (someEnum) =>
{
switch (someEnum)
{
case ESomeEnum.UseStrategyA:
return cc.Resolve<ConcreteStrategyA>();
case ESomeEnum.UseStrategyB:
return cc.Resolve<ConcreteStrategyB>();
default:
throw new ArgumentException();
}
};
});
var container = builder.Build();
container.Resolve<Context>().DoSomething();
Если стратегии не потребляют никаких зависимостей, которые зарегистрированы в контейнере, вы можете новых их самостоятельно, и упростить конфигурацию, как это:
var builder = new ContainerBuilder();
builder.RegisterType<Context>().AsSelf().SingleInstance();
builder.Register<Func<ESomeEnum, IStrategy>>(c => StrategyFactory.GetStrategy);
var container = builder.Build();
container.Resolve<Context>().DoSomething();
И иметь дело переключателя в отдельном классе:
public static class StrategyFactory
{
internal static IStrategy GetStrategy(ESomeEnum someEnum)
{
switch (someEnum)
{
case ESomeEnum.UseStrategyA:
return new ConcreteStrategyA();
case ESomeEnum.UseStrategyB:
return new ConcreteStrategyB();
default:
throw new ArgumentException();
}
}
}
работающий полный пример кода - check in .NET Fiddle:
using Autofac;
using System;
using System.Reflection;
namespace Samples.Autofac.StrategyPattern
{
public interface IStrategy { void Do(); }
public class ConcreteStrategyA : IStrategy { public void Do() { Console.WriteLine("Called ConcreteStrategyA.Do()"); } };
public class ConcreteStrategyB : IStrategy { public void Do() { Console.WriteLine("Called ConcreteStrategyB.Do()"); } };
public enum ESomeEnum
{
UseStrategyA, UseStrategyB,
}
public class Context
{
private readonly Func<ESomeEnum, IStrategy> _strategyFactory;
public Context(Func<ESomeEnum, IStrategy> strategyFactory)
{
_strategyFactory = strategyFactory;
}
public void DoSomething()
{
_strategyFactory(ESomeEnum.UseStrategyB).Do();
_strategyFactory(ESomeEnum.UseStrategyA).Do();
}
}
public class AutofacExample
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<Context>().AsSelf().SingleInstance();
builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(IStrategy)))
.Where(t => typeof(IStrategy).IsAssignableFrom(t))
.AsSelf();
builder.Register<Func<ESomeEnum, IStrategy>>(c =>
{
var cc = c.Resolve<IComponentContext>();
return (someEnum) =>
{
switch (someEnum)
{
case ESomeEnum.UseStrategyA:
return cc.Resolve<ConcreteStrategyA>();
case ESomeEnum.UseStrategyB:
return cc.Resolve<ConcreteStrategyB>();
default:
throw new ArgumentException();
}
};
});
var container = builder.Build();
container.Resolve<Context>().DoSomething();
}
}
}
У вас нет.
Контейнер не должен содержать бизнес-правил. Если да, то нет простого способа понять, какой класс вы получаете и когда. Читайте о наименее удивительном принципе.
Вместо этого вы должны создать новый класс, цель которого - решить, какую стратегию использовать. Контракт на него должен быть примерно таким:
public interface IMyStrategyChooser
{
IBase GetBestStrategyFor(YourEnum enum);
}
Создайте реализацию для этого класса и возьмите интерфейс как зависимость.
Я не согласен с тем, что вы не должны. Что делать, если ваши стратегии содержат зависимости, которыми управляет ваш контейнер IoC? –
Я вижу это из вашего ответа;) Что касается вашего вопроса: используйте контейнер (местоположение службы) внутри своего выбора стратегии. На самом деле это не имеет никакого отношения к вашей функции, за исключением того, что конкретный класс получает эту ответственность, а не создает в контейнере много кода инфраструктуры. – jgauffin
Вот что показывает мой пример :-). Стратегии и завод должны быть зарегистрированы в контейнере IoC, если контейнер IoC будет решать зависимости в стратегиях. –
является 'someEnum' настройкой конфигурации? или значение времени выполнения? –
Возможный дубликат [Лучший способ использования StructureMap для реализации шаблона стратегии] (http://stackoverflow.com/questions/1499442/best-way-to-use-structuremap-to-implement-strategy-pattern) – NightOwl888