我想加入战略模式和DI。
class A : IBase
{
public void Do();
}
class B : IBase
{
public void Do();
}
interface IBase
{
void Do();
}
class Context()
{
private _usedClass;
void SetClass(IBase usedClass)
{
_usedClass = usedClass;
}
public void Do()
{
_usedClass.Do();
}
}
void Main()
{
var context = new Context();
var someEnum = SomeMethod();
//how to use here DI resolve to get appropriate class instead of if/else?
if (someEnum == MyEnum.A)
context.SetClass(new A());
else if (someEnum == MyEnum.B)
context.SetClass(new B());
context.Do();
}
如何在这里使用DI解析得到适当的类而不是if / else? 感谢
答案 0 :(得分:4)
您可以使用密钥服务查找(Autofac Docs)并创建一个简单的工厂,从枚举密钥中解析正确的类型。
首先配置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是根据在config ...中设置为枚举的类注入的。
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;
}
}
答案 1 :(得分:2)
我肯定会使用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();
}
}
}
答案 2 :(得分:0)
我使用适配器:
builder.RegisterType<A>().Keyed<IBase>(MyEnum.A);
builder.RegisterType<B>().Keyed<IBase>(MyEnum.B);
builder.RegisterAdapter<IIndex<MyEnum, IBase>,
IDictionary<MyEnum, IBase>>(idx =>
{
var d = new Dictionary<MyEnum, IBase>();
foreach (MyEnum e in Enum.GetValues(typeof(MyEnum)))
{
d[e] = idx[e];
}
return d;
});
现在您可以在构造函数中注入IDictionary<MyEnum, IBase>
:
class Context()
{
private IDictionary<MyEnum, IBase> _baseDictionary;
public Context(IDictionary<MyEnum, IBase> baseDictionary)
{
_baseDictionary = baseDictionary;
}
public void Do(MyEnum strategy)
{
_baseDictionary[strategy].Do();
}
}
我喜欢这个,因为围绕使用这些类的代码编写测试更容易,因为我只使用BCL类型。