使用相同的接口调用多个类
Call multiple classes with the same interface
我有一个类似
的界面
public interface IAddressProvider
{
string GetAddress(double lat, double long);
}
在我的消费 class 中,我想循环使用具体的提供者,直到得到结果,例如(简化):
string address;
address = _cachedAddressProvider.GetAddress(lat, long);
if(address == null)
address = _localDbAddressProvider.GetAddress(lat, long);
if(address = null)
address = _externalAddressProvider.GetAddress(lat, long);
return address ?? "no address found";
然后我可以模拟每个提供程序进行单元测试,将 null 设置为 return 值以适当地测试所有代码路径。
我如何将接口注入我的消费 class(最好使用 StructureMap)以便正确解析每个具体实现?
我对 StructureMap 不是特别熟悉,但据我所知有两种解决方案。
1) 命名实例 - 您使用 StructureMap 将 IAddressProvider
的 3 个具体实现注册为命名实例,然后配置构造函数参数。
StructureMap 命名实例配置:http://docs.structuremap.net/InstanceExpression.htm#section14
在构造函数注入中使用命名参数:http://lookonmyworks.co.uk/2011/10/04/using-named-instances-as-constructor-arguments/
2) 更多接口 - 假设只有几个 IAddressProvider
实现而不是数百个,您可以创建 ICachedAddressProvider
、ILocalDbAddressProvider
和 IExternalAddressProvider
实现 IAddressProvider
然后在消费 class.
的构造函数中使用它们
如果可能有明显更具体的 IAddressProvider
实现,那么您可能想要研究一些类似于抽象工厂的东西。
你能不能只用container.GetAllInstances
?,像这样:
var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
address.add(provider.GetAddress(lat, long));
}
编辑:
我明白你的意思了。如果您使用的是 StructureMap 2.x,那么我建议您查看 Conditionally
子句。然而,这 been removed in version 3 支持创建您自己的构建器 class,它应该负责返回正确的实例。
例如:
public class AddressProviderBuilder : IInstanceBuilder
{
private readonly IContainer container;
public AddressProviderBuilder(IContainer container)
{
this.container = container;
}
public IAddressProvider Build()
{
foreach (var provider in this.container.GetAllInstances<IAddressProvider>())
{
if (provider.GetAddress(lat, long) != null)
{
return provider;
}
}
return null;
}
}
您有多个地址提供者这一事实不是调用代码必须处理的事情。因此,创建一个特定的提供者代理来处理这些多个提供者。
像这样。
public interface IAddressProvider {
string GetAddress(double lat, double long);
}
public class AddressProviderProxy: IAddressProvider {
public AddressProviderProxy(IAddressProvider[] providers) {
_providers = providers; // TODO: Add a NULL guard
}
private readonly IAddressProvider[] _providers;
string IAddressProvider.GetAddress(double lat, double long) {
foreach (var provider in _providers) {
string address = provider.GetAddress(lat, long);
if (address != null)
return address;
}
return null;
}
}
// Wire up using DI
container.Register<IAddressProvider>(
() => new AddressProviderProxy(
new IAddressProvider[3] {
cachedAddressProvider,
localDbAddressProvider,
externalAddressProvider
}
)
);
// Use it
IAddressProvider provider = ...from the container, injected..
string address = provider.GetAddress(lat, long) ?? "no address found";
我有一个类似
的界面public interface IAddressProvider
{
string GetAddress(double lat, double long);
}
在我的消费 class 中,我想循环使用具体的提供者,直到得到结果,例如(简化):
string address;
address = _cachedAddressProvider.GetAddress(lat, long);
if(address == null)
address = _localDbAddressProvider.GetAddress(lat, long);
if(address = null)
address = _externalAddressProvider.GetAddress(lat, long);
return address ?? "no address found";
然后我可以模拟每个提供程序进行单元测试,将 null 设置为 return 值以适当地测试所有代码路径。
我如何将接口注入我的消费 class(最好使用 StructureMap)以便正确解析每个具体实现?
我对 StructureMap 不是特别熟悉,但据我所知有两种解决方案。
1) 命名实例 - 您使用 StructureMap 将 IAddressProvider
的 3 个具体实现注册为命名实例,然后配置构造函数参数。
StructureMap 命名实例配置:http://docs.structuremap.net/InstanceExpression.htm#section14
在构造函数注入中使用命名参数:http://lookonmyworks.co.uk/2011/10/04/using-named-instances-as-constructor-arguments/
2) 更多接口 - 假设只有几个 IAddressProvider
实现而不是数百个,您可以创建 ICachedAddressProvider
、ILocalDbAddressProvider
和 IExternalAddressProvider
实现 IAddressProvider
然后在消费 class.
如果可能有明显更具体的 IAddressProvider
实现,那么您可能想要研究一些类似于抽象工厂的东西。
你能不能只用container.GetAllInstances
?,像这样:
var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
address.add(provider.GetAddress(lat, long));
}
编辑:
我明白你的意思了。如果您使用的是 StructureMap 2.x,那么我建议您查看 Conditionally
子句。然而,这 been removed in version 3 支持创建您自己的构建器 class,它应该负责返回正确的实例。
例如:
public class AddressProviderBuilder : IInstanceBuilder
{
private readonly IContainer container;
public AddressProviderBuilder(IContainer container)
{
this.container = container;
}
public IAddressProvider Build()
{
foreach (var provider in this.container.GetAllInstances<IAddressProvider>())
{
if (provider.GetAddress(lat, long) != null)
{
return provider;
}
}
return null;
}
}
您有多个地址提供者这一事实不是调用代码必须处理的事情。因此,创建一个特定的提供者代理来处理这些多个提供者。
像这样。
public interface IAddressProvider {
string GetAddress(double lat, double long);
}
public class AddressProviderProxy: IAddressProvider {
public AddressProviderProxy(IAddressProvider[] providers) {
_providers = providers; // TODO: Add a NULL guard
}
private readonly IAddressProvider[] _providers;
string IAddressProvider.GetAddress(double lat, double long) {
foreach (var provider in _providers) {
string address = provider.GetAddress(lat, long);
if (address != null)
return address;
}
return null;
}
}
// Wire up using DI
container.Register<IAddressProvider>(
() => new AddressProviderProxy(
new IAddressProvider[3] {
cachedAddressProvider,
localDbAddressProvider,
externalAddressProvider
}
)
);
// Use it
IAddressProvider provider = ...from the container, injected..
string address = provider.GetAddress(lat, long) ?? "no address found";