2

Я хотел бы присоединиться к использованию шаблона стратегии и DI.Как использовать шаблон стратегии вместе с инъекцией зависимостей (autofac)

Как использовать здесь Разрешение DI для получения соответствующего класса вместо if/else? Спасибо

+0

является 'someEnum' настройкой конфигурации? или значение времени выполнения? –

+0

Возможный дубликат [Лучший способ использования StructureMap для реализации шаблона стратегии] (http://stackoverflow.com/questions/1499442/best-way-to-use-structuremap-to-implement-strategy-pattern) – NightOwl888

ответ

2

Вы можете использовать поиск по ключевым словам (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; 
     } 
    } 
+0

Вы должны использовать инсталляция конструктора для вашего интерфейса IBase в конструкторе Context. Используя метод setter, вы игнорируете принцип инверсии управления, поскольку он сам обеспечивает зависимость вместо контейнера IoC. –

+0

Если вы заметили, что на заводе используется конструктор. Контекст - это не тот вопрос, о котором идет речь. – dbugger

0

Я бы определенно использовать 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(); 
     } 
    } 
} 
1

У вас нет.

Контейнер не должен содержать бизнес-правил. Если да, то нет простого способа понять, какой класс вы получаете и когда. Читайте о наименее удивительном принципе.

Вместо этого вы должны создать новый класс, цель которого - решить, какую стратегию использовать. Контракт на него должен быть примерно таким:

public interface IMyStrategyChooser 
{ 
    IBase GetBestStrategyFor(YourEnum enum); 
} 

Создайте реализацию для этого класса и возьмите интерфейс как зависимость.

+0

Я не согласен с тем, что вы не должны. Что делать, если ваши стратегии содержат зависимости, которыми управляет ваш контейнер IoC? –

+0

Я вижу это из вашего ответа;) Что касается вашего вопроса: используйте контейнер (местоположение службы) внутри своего выбора стратегии. На самом деле это не имеет никакого отношения к вашей функции, за исключением того, что конкретный класс получает эту ответственность, а не создает в контейнере много кода инфраструктуры. – jgauffin

+0

Вот что показывает мой пример :-). Стратегии и завод должны быть зарегистрированы в контейнере IoC, если контейнер IoC будет решать зависимости в стратегиях. –

Смежные вопросы