使用同一接口调用多个类

本文关键字:调用 接口 | 更新日期: 2023-09-27 18:36:39

我有一个像这样的接口

public interface IAddressProvider
{
    string GetAddress(double lat, double long);
}

在我的消费类中,我想循环浏览具体的提供程序,直到得到一个结果,例如(简化):

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 设置为返回值以适当地测试所有代码路径。

如何将接口注入到我的消费类中(最好使用 StructureMap),以便正确解析每个具体实现?

使用同一接口调用多个类

您有多个地址提供程序的事实不是调用代码应该处理的事情。因此,创建一个特定的提供程序代理来处理这些多个提供程序。

喜欢这个。

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";

你能不能只用container.GetAllInstances?,像这样:

var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
    address.add(provider.GetAddress(lat, long));
}

编辑:

我现在明白你的意思了。如果您使用的是StructureMap 2.x,那么我建议您查看Conditionally子句。但是,在版本 3 中已将其删除,以便创建自己的构建器类,该类应负责返回正确的实例。

例如:

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;
    }
}

我并不特别熟悉StructureMap,但据我所知,有两种解决方案。

1) 命名实例 - 使用 StructureMap 将 IAddressProvider 的 3 个具体实现注册为命名实例,然后配置构造函数参数。

结构映射命名实例配置:http://docs.structuremap.net/InstanceExpression.htm#section14

在构造函数注入中使用命名参数:http://lookonmyworks.co.uk/2011/10/04/using-named-instances-as-constructor-arguments/

2)更多接口 - 假设只有几个IAddressProvider实现而不是数百个,你可以创建一个实现IAddressProviderICachedAddressProviderILocalDbAddressProviderIExternalAddressProvider,然后在消费类的构造函数中使用它们。

如果可能有更具体的IAddressProvider实现,那么您可能希望按照抽象工厂的思路进行研究。