C# 使用同一接口调用多个类
我有一个像C# 使用同一接口调用多个类,c#,unit-testing,dependency-injection,structuremap,C#,Unit Testing,Dependency Injection,Structuremap,我有一个像 public interface IAddressProvider { string GetAddress(double lat, double long); } 在我的消费类中,我希望循环遍历具体的提供者,直到得到一个结果,如(简化): 然后,我可以模拟每个提供程序进行单元测试,将null设置为返回值以适当地测试所有代码路径 如何将接口注入到我的消费类中(最好使用StructureMap),以便正确解析每个具体实现?我不太熟悉StructureMap,但据我所知,有两种解
public interface IAddressProvider
{
string GetAddress(double lat, double long);
}
在我的消费类中,我希望循环遍历具体的提供者,直到得到一个结果,如(简化):
然后,我可以模拟每个提供程序进行单元测试,将null设置为返回值以适当地测试所有代码路径
如何将接口注入到我的消费类中(最好使用StructureMap),以便正确解析每个具体实现?我不太熟悉StructureMap,但据我所知,有两种解决方案 1) 命名实例-将StructureMap注册为命名实例,然后配置构造函数参数 StructureMap命名实例配置: 在构造函数注入中使用命名参数: 2) 更多接口-假设只有少数
IAddressProvider
实现,而不是数百个,您可以创建一个ICachedAddressProvider
,ilocaldaddressprovider
,和iexternaladressprovider
,它们实现IAddressProvider
,然后在消费类的构造函数中使用它们
如果可能有更具体的实现,那么您可能希望按照抽象工厂的思路进行研究。您是否可以不使用容器。GetAllInstances
var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
address.add(provider.GetAddress(lat, long));
}
您有多个地址提供程序这一事实并不是调用代码必须处理的。因此,创建一个特定的提供者代理来处理这些多个提供者 像这样
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";
公共接口提供程序{
字符串GetAddress(双lat,双long);
}
公共类AddressProviderProxy:IAddressProvider{
公共地址ProviderProxy(IAddressProvider[]提供程序){
_providers=providers;//TODO:添加空保护
}
私有只读提供程序[]\u提供程序;
字符串IAddressProvider.GetAddress(双lat,双long){
foreach(变量提供程序在_提供程序中){
字符串地址=provider.GetAddress(lat,long);
if(地址!=null)
回信地址;
}
返回null;
}
}
//使用DI连接
集装箱。登记(
()=>新地址提供者代理(
新供应商[3]{
缓存提供程序,
localDbAddressProvider,
外部地址提供者
}
)
);
//使用它
IAddressProvider=…来自容器,已注入。。
字符串地址=provider.GetAddress(lat,long)??“找不到地址”;
我想返回第一个非空地址,这样我就不会花费访问每个提供程序的费用(因此,请先缓存,如果没有缓存,则本地db,如果没有本地db条目,则外部提供程序)。我已更新了我的答案,希望它会更有帮助。这甚至可以在不需要IContainer依赖项的情况下完成。SM支持注入集合,因此您只需在构造函数中添加依赖项:public AddressProviderBuilder(IAddressProvider[]providers)
,就可以获得所有已注册的IAddressProvider实现。我喜欢这种方法,但我现在如何模拟我的具体提供程序来测试AddressProviderProxy首先调用缓存,如果缓存为空,则为本地数据库添加一个带有多个模拟的测试。您可以分别测试每个提供程序(包括代理)的逻辑。代理的逻辑是,它生成生成地址的第一个提供程序的地址,因此在第一个提供程序生成地址时添加单元测试,在第二个提供程序生成地址时添加单元测试,在多个提供程序生成地址时添加单元测试,等等。您不应该测试实际提供者与代理的组合。
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";