我想给我的类调用者按名称选择提供者,而不是像标准DI推荐的那样传递提供者具体类。它将允许隐藏客户端的实际实现细节,仍然可以控制使用哪个提供程序。我们通过实施工厂
来完成它public ICurrencyProvider GetCurrencyServiceProvider(string providerName)
{
switch (providerName)
{
case "CurrencyLayerAPI":
{ currencyService = new CurrencyLayerWrapper(); }
break;
case "XE":
{ currencyProvider = new XEWrapper(); }
break;
}
return _currencyProvider;
}
和constuctor期望providerName作为参数。
然而,对于单元测试,我希望使用替代,而不是具体的提供者类。 我最终得到了2个参数,负责生产代码的相同选择名称和测试调用的接口。
public CurrencyProcessor(string providerName, ICurrencyProvider substituteCurrencyProvider =null)
{
if(!providerName .IsNullOrEmpty())
{
_currencyProvider = GetCurrencyServiceProvider(providerName);
}
else
{ _currencyProvider =substituteCurrencyProvider;
}
}
稍微替代的实现是从配置中读取providerName,而不是将其作为参数传递。
public CurrencyProcessor(IConfigurationProvider configurationProvider, ICurrencyProvider substituteCurrencyProvider =null)
{
_providerName = _configurationProvider.GetAppSetting("CurrencyProviderToUse");
if(!providerName .IsNullOrEmpty())
{
_currencyProvider = GetCurrencyServiceProvider(providerName);
}
else
{ _currencyProvider =substituteCurrencyProvider;
}
}
我徘徊,是否有更好的方法可以让单个参数来控制内部对象的创建,但是避免承担向客户端创建对象的责任。
相关讨论
How to use Dependency Injection without breaking encapsulation?
Preferable way of making code testable: Dependency injection vs encapsulation
https://softwareengineering.stackexchange.com/questions/344442/dependency-injection-with-default-construction
答案 0 :(得分:1)
因为在你的构造函数中你是静态创建你的提供者,只需注入提供者。
按照你的描述创建一个工厂......
public class CurrencyFactory
{
public static ICurrencyProvider GetCurrencyServiceProvider(string providerName)
{
return null;
}
}
然后使用标准依赖注入: -
public class CurrencyProcessor
{
private ICurrencyProvider _currencyProvider;
public CurrencyProcessor(ICurrencyProvider currencyProvider)
{
_currencyProvider = currencyProvider;
}
}
然后像这样使用
var p = new CurrencyProcessor(CurrencyFactory.GetCurrencyServiceProvider("bitcoin"));
然后在你的测试中嘲笑它
var mock = new Mock<ICurrencyProvider>(). // mock stuff
答案 1 :(得分:0)
不确定我是否理解正确。
对我来说,听起来你想拥有2个不同的工厂。
首先创建一个接口:
public interface ICurrencyProviderFactory
{
ICurrencyProvider Create()
}
然后创建配置工厂:
public class ConfigurationCurrencyProviderFactory : ICurrencyProviderFactory
{
public ConfigurationCurrencyProviderFactory(IConfigurationProvider configuration)
{
}
public ICurrencyProvider Create()
{
}
}
然后是UnitTest工厂:
public class UnitTestCurrencyProviderFactory : ICurrencyProviderFactory
{
public UnitTestCurrencyProviderFactory()
{
}
public ICurrencyProvider Create()
{
}
}
您的货币处理器应如下所示:
public CurrencyProcessor(ICurrencyProviderFactory factory)
{
_currencyProvider = factory.Create();
}
在ServiceCollection中,或者在解决依赖关系时,应该包含正确的工厂。
因此,对于Production,您为UnitTest ConfigurationCurrencyProviderFactory
添加UnitTestCurrencyProviderFactory
。那么您的实际代码应该取决于ICurrencyProviderFactory
。
答案 2 :(得分:0)
您实际需要与工厂一起申请的是战略模式
interface ICurrencyProvider {
//...members
}
public interface ICurrencyProviderStrategy {
string Name { get; }
ICurrencyProvider Create();
}
public interface ICurrencyProviderFactory {
ICurrencyProvider GetCurrencyServiceProvider(string providerName);
}
工厂的实施将取决于要求创建所需类型的策略集合。
public class CurrencyProviderFactory : ICurrencyProviderFactory {
private readonly IEnumerable<ICurrencyProviderStrategy> strategies;
public CurrencyProviderFactory(IEnumerable<ICurrencyProviderStrategy> strategies) {
this.strategies = strategies;
}
public ICurrencyProvider GetCurrencyServiceProvider(string providerName) {
var provider = strategies.FirstOrDefault(p => p.Name == providerName);
if (provider != null)
return provider.Create();
return null;
}
}
这可以提供更大的灵活性,因为可以注入任何数量的策略。
以下是CurrencyLayerWrapper
策略的示例
public class CurrencyLayerWrapperProvider : ICurrencyProviderStrategy {
public string Name { get { return "CurrencyLayerAPI"; } }
public ICurrencyProvider Create() {
return new CurrencyLayerWrapper();
}
}