C# 您在依赖项中寻找什么来确定它是否应该是注入依赖项?

C# 您在依赖项中寻找什么来确定它是否应该是注入依赖项?,c#,.net,dependency-injection,C#,.net,Dependency Injection,我很难弄清楚何时应该注入依赖项。让我们仅使用我的项目中的一个简单示例: class CompanyDetailProvider : ICompanyDetailProvider { private readonly FilePathProvider provider; public CompanyDetailProvider(FilePathProvider provider) { this.provider = provider; } publ

我很难弄清楚何时应该注入依赖项。让我们仅使用我的项目中的一个简单示例:

class CompanyDetailProvider : ICompanyDetailProvider {
    private readonly FilePathProvider provider;
    public CompanyDetailProvider(FilePathProvider provider) {
        this.provider = provider;
    }

    public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date) {
        string path = this.provider.GetCompanyDetailFilePathForDate(date);
        var factory = new DataReaderFactory();
        Func<IDataReader> sourceProvider = () => factory.CreateReader(
            DataFileType.FlatFile, 
            path
        );
        var hydrator = new Hydrator<CompanyDetail>(sourceProvider);
        return hydrator;
    }
}
class CompanyDetailProvider:ICompanyDetailProvider{
私有只读文件路径提供程序;
public CompanyDetailProvider(FilePathProvider提供程序){
this.provider=提供者;
}
public IEnumerable GetCompanyDetailsForDate(日期时间日期){
字符串路径=this.provider.GetCompanyDetailFilePathForDate(日期);
var factory=new DataReaderFactory();
Func sourceProvider=()=>factory.CreateReader(
DataFileType.FlatFile,
路径
);
var hydrator=新hydrator(sourceProvider);
返回水合器;
}
}
(不是生产质量!)

ICompanyDetailProvider
负责为消费者提供
CompanyDetail
s的实例。具体实现
CompanyDetailProvider
是通过使用
Hydrator
从文件中水合
CompanyDetail
的实例来实现的,Hydrator使用反射来填充来自
IDataReader
T
的实例。显然,
CompanyDetailProvider
依赖于
DataReaderFactory
(它返回给定文件路径的
OleDbDataReader
实例)和
DataReader
。应该注入这些依赖关系吗?插入
文件路径提供程序
正确吗?我应该检查哪些品质来决定是否应该注射

如何确定类是否应该使用依赖项注入
这个类需要外部依赖项吗

如果是,注射

如果否,则没有依赖项

回答“注入FilePathProvider是否正确?”是的,它是正确的

编辑:为了澄清,任何外部依赖都是指调用不相关但依赖的类,特别是当它涉及物理资源(如从磁盘读取文件路径)时,但这也意味着任何类型的服务或模型类的逻辑与类的核心功能无关


一般来说,只要你打电话给新的接线员,就可以猜测到这一点。在大多数情况下,当新操作符必须处理除数据传输对象以外的任何类时,您都希望重构掉它的所有用法。当类位于使用位置的内部时,如果新语句降低了复杂性(如new DataReaderFactory()),则可以使用新语句然而,对于构造函数注入来说,这似乎也是一个非常好的选择。

我倾向于更自由地注入依赖项,因此我肯定希望同时注入IDataReader以摆脱新的DataFactoryReader和Hyderator。它使所有东西都更加松散地耦合在一起,这当然使它更易于维护


另一个很容易马上实现的好处是更好的可测试性。您可以创建IDataReader和Hyderator的模拟,将单元测试隔离为GetCompanyDetailsForDate方法,而不必担心datareader和Hyderator内部会发生什么。

我通过意图/机制镜头评估依赖项的使用点:这段代码是否清楚地传达了其意图,还是我必须从一堆实现细节中提取出来

如果代码看起来确实像一堆实现细节,我将确定输入和输出,并创建一个全新的依赖项来表示所有“如何”背后的原因。然后,我将复杂性推到新的依赖项中,使原始代码更简单、更清晰

当我阅读这个问题中的代码时,我清楚地看到了基于日期的文件路径检索,后面是一组不透明的语句,这些语句没有明确传达在特定路径上读取特定类型实体的目标。我可以努力克服它,但那会使我步履蹒跚

我建议您在获得路径后,提高计算后半部分的抽象级别。首先,我将定义一个实现代码输入/输出的依赖项:

public interface IEntityReader
{
    IEnumerable<T> ReadEntities<T>(string path);
}
公共接口IEntityReader
{
IEnumerable可读实体(字符串路径);
}
然后,使用此接口重写原始类:

public sealed class CompanyDetailProvider : ICompanyDetailProvider
{
    private readonly IFilePathProvider _filePathProvider;
    private readonly IEntityReader _entityReader;

    public CompanyDetailProvider(IFilePathProvider filePathProvider, IEntityReader entityReader)
    {
        _filePathProvider = filePathProvider;
        _entityReader = entityReader;
    }

    public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date)
    {
        var path = _filePathProvider.GetCompanyDetailsFilePathForDate(date);

        return _entityReader.ReadEntities<CompanyDetail>(path);
    }
}
公共密封类CompanyDetailProvider:ICompanyDetailProvider
{
专用只读IFilePathProvider\u filePathProvider;
私有只读客户端Reader _entityReader;
public CompanyDetailProvider(IFilePathProvider filePathProvider、IEntityReader entityReader)
{
_filePathProvider=filePathProvider;
_entityReader=entityReader;
}
public IEnumerable GetCompanyDetailsForDate(日期时间日期)
{
var path=\u filePathProvider.GetCompanyDetailsFilePathForDate(日期);
返回_entityReader.ReadEntities(路径);
}
}
现在,您可以对血淋淋的细节进行沙箱处理,这些细节在隔离中变得非常紧密:

public sealed class EntityReader : IEntityReader
{
    private readonly IDataReaderFactory _dataReaderFactory;

    public EntityReader(IDataReaderFactory dataReaderFactory)
    {
        _dataReaderFactory = dataReaderFactory;
    }

    public IEnumerable<T> ReadEntities<T>(string path)
    {
        Func<IDataReader> sourceProvider =
            () => _dataReaderFactory.CreateReader(DataFileType.FlatFile, path);

        return new Hydrator<T>(sourceProvider);
    }
}
公共密封类EntityReader:EntityReader
{
私有只读IDataReaderFactory\u dataReaderFactory;
公共实体读卡器(IDataReaderFactory dataReaderFactory)
{
_dataReaderFactory=dataReaderFactory;
}
公共IEnumerable可读实体(字符串路径)
{
函数源提供程序=
()=>_dataReaderFactory.CreateReader(DataFileType.FlatFile,路径);
返回新的查询器(sourceProvider);
}
}

如本例所示,我认为您应该将数据读取器工厂抽象出来,并直接实例化读取器。区别在于
EntityReader
使用数据读取器工厂,而它只创建数据读取器。它实际上根本不依赖于实例;相反,它是一个工厂。

你说的“外部依赖”是什么意思?修饰语“external”对“dependency”有什么意义?对外部资源(如dat)的依赖性