【问题标题】:A problem implementing Dependency Injection and Interfaces实现依赖注入和接口的问题
【发布时间】:2020-10-09 15:32:53
【问题描述】:

我正在尝试了解依赖注入和接口,但遇到了一个奇怪的问题。

https://dotnetfiddle.net/5vfcd0

我有一个接口和一个实现该接口的模型:

public class FileModel : IFileModel
{
    public string Filepath {get;set;}
}

public interface IFileModel
{
    string Filepath {get;set;}
}

我还有一个 FileProcessor 类,它处理实现 IFileModel 接口的对象:

public class FileProcessor
{
    private static IFileModel _file;

    public FileProcessor(IFileModel file)
    {
        _file = file;
    }

    public List<IFileModel> ProcessFiles(List<string> filepaths)
    {
        List<IFileModel> output = new List<IFileModel>();

        foreach(string path in filepaths)
        {
            _file.Filepath = path;
            output.Add(_file);
        }

        return output;
    }
}

在我的 Main() 方法中,我创建了一个虚拟文件路径列表。然后,我创建 FileProcessor 的一个实例,并将 FileModel 的一个新实例作为参数提供给 FileProcessor 的构造函数。我这样做是为了向 FileProcessor 指定我要处理的文件类型。

我调用 FileProcessor 实例的 ProcessFiles() 方法,该方法将文件路径列表作为参数。 ProcessFiles() 循环遍历文件路径并创建 IFileModel 对象列表并返回该列表。

public static void Main()
{
    List<string> filepaths = new List<string>();
    filepaths.Add("path1");
    filepaths.Add("path2");
    filepaths.Add("path3");
    filepaths.Add("path4");
    filepaths.Add("path5");

    FileProcessor fileProcessor = new FileProcessor(new FileModel());

    List<IFileModel> files = fileProcessor.ProcessFiles(filepaths); 

    foreach(var file in files)
    {
        Console.WriteLine(file.Filepath);
    }
}

我的假设是,在 Main() 方法的末尾,我会有一个 List,其中该列表中的每个对象都有不同的文件路径。

foreach(var file in files)
{
    Console.WriteLine(file.Filepath);
}

output:
path1
path2
path3
path4
path5

相反,由 ProcessFiles() 方法返回的列表中的每个对象都填充有该方法循环通过的 last 文件路径的文件路径。结果是这样的:

path5
path5
path5
path5
path5

我创建了一个小提琴来演示这个问题。 https://dotnetfiddle.net/5vfcd0

如果我在 ProcessFiles() 方法中实例化一个新的 FileModel,它会按预期工作。但我不希望 FileProcessor 依赖于 IFileModel 的特定实现。

public List<IFileModel> ProcessFiles(List<string> filepaths)
{
    List<IFileModel> output = new List<IFileModel>();

    foreach(string path in filepaths)
    {
        _file = new FileModel(); // dependency
        _file.Filepath = path;
        output.Add(_file);
    }

    return output;
}

我做错了什么?我应该如何修复这个实现,使 FileProcessor 不依赖于任何实现 IFileModel 接口的特定类?

【问题讨论】:

  • 那是因为您只有 1 个对象,并且它具有状态,您可以更改循环的每次迭代。如果您的意图是 a) 不必在循环中构造新的此类 FileModel 对象,因为您随后需要处理依赖关系,并且 b) 仍然允许获取多个独立 FileModel 对象,而不是依赖 FileModel 对象, 依赖于一个工厂,该工厂可以被要求在给定路径的情况下按需生成 FileModel 对象。
  • 创建工厂。它的唯一职责是根据路径创建 IFileModel
  • 换句话说,依赖IFileModelFactory,它有一个类似IFileModel Create(string path);的方法,它在内部以正确的方式构造正确的对象。
  • 感谢您的 cmets 和帮助。我将 Johnathan 的答案标记为正确,因为他提供了一些代码示例。我会赞成 Lasse 的回答,但我没有足够的声望点。
  • 你为什么要把FileModel隐藏在一个抽象后面呢? FileModel 似乎是一个没有行为的以数据为中心的对象。在您的情况下,将其隐藏在抽象后面似乎不是很有用。相反,我建议让ProcessFiles 自己创建新的FileModel 实例;这大大简化了您的解决方案。

标签: c# dependency-injection interface


【解决方案1】:

您目前正在将一个 FileModel 实例传递给您的 FileProcessor 类,并将其添加到列表中 5 次。

每次,您都在更新同一实例的Filepath

您可能想要做的是注入工厂方法:

public class FileProcessor
{
    private static Func<IFileModel> _fileFactory;

    public FileProcessor(Func<IFileModel> fileFactory)
    {
        _fileFactory = fileFactory;
    }

    public List<IFileModel> ProcessFiles(List<string> filepaths)
    {
        List<IFileModel> output = new List<IFileModel>();

        foreach(string path in filepaths)
        {
            var file = _fileFactory();
            file.Filepath = path;
            output.Add(file);
        }

        return output;
    }
}

然后调用如下:

var fileProcessor = new FileProcessor(() => new FileModel());

或者更好的是,为你的工厂创建一个接口:

interface IFileModelFactory
{
    FileModel CreateModel();
}

实现:

class FileModelFactory
{
    public FileModel CreateModel() => new FileModel();
}

并注入:

public class FileProcessor
{
    private static IFileModelFactory _fileFactory;

    public FileProcessor(IFileModelFactory fileFactory)
    {
        _fileFactory = fileFactory;
    }

    public List<IFileModel> ProcessFiles(List<string> filepaths)
    {
        List<IFileModel> output = new List<IFileModel>();

        foreach(string path in filepaths)
        {
            var file = _fileFactory.CreateModel();
            file.Filepath = path;
            output.Add(file);
        }

        return output;
    }
}

【讨论】:

  • 由于代码示例而标记为答案。我确实喜欢 FactoryMethod 方法,因为它很简单,并且因为它教我第一次实际使用 Func。虽然我同意,但做工厂的接口/实现可能会更“正确”。
猜你喜欢
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多