我应该在哪里存储对象集合以方便DI /测试?

时间:2012-08-17 14:32:39

标签: c# dependency-injection factory-pattern

我试图找出存储从数据库中检索的对象集合的位置,这些对象是应用程序运行所必需的。例如,我正在尝试重新设计使用ReportParameter对象的报表应用程序。这些对象以XML格式存储在数据库中。在最简单的级别,定义的集合可以存储为Dictionary<string, string>(对象键,对象定义XML)。

我想实现一个工厂模式来处理从XML定义中创建这些对象。

IReport GetReport(string reportName)
IParameter GetParameter(string parameterName)

这些方法要求工厂存储报告和参数的集合。

所以,我的问题是:

  1. 工厂应该完全存储对象集合,还是仅负责从定义中创建对象,将方法更改为IReport GetReport(string reportDefinition)
  2. 如果工厂应该存储对象,我应该简单地将集合注入工厂类的构造函数中吗?
  3. 单元测试 - 如果我走#2的路线,我想我会把我的测试集合注入工厂,对吗?

3 个答案:

答案 0 :(得分:2)

您的应用程序应该是持久无知的,因此您的报表工厂(或存储库对于数据存储的外观来说是一个更好的术语,实际上它是数据访问对象而不是真正的存储库)不应采用报表定义格式。

Get方法应该为报告采用唯一标识符(这应该是报告名称吗?)并返回包含报告实体的所有值的DTO类型。

您可以使用抽象工厂模式来实现存储库的不同实现,执行XML反序列化的实现,以及单元测试的模拟实现。

如果您已经有一个数据库作为数据存储,那么您的存储库不需要将集合存储在内存中,尽管您可能希望将缓存添加到存储库中,如果您的应用程序正在运行,则再次更容易切换抽象。

答案 1 :(得分:2)

您可以通过多种方式对其进行结构化,这些方式都符合SRP (Single Responsibility Principle)

一种方法如下:拥有一个接口IReportLoadingService,其中包含一个接收报告标识符的方法,并返回IReport实例。

IReportLoadingService的实现可以将IReportDefinitionRetrievalService作为依赖项,例如

public class ReportLoadingService : IReportLoadingService
{
    private readonly IReportDefinitionRetrievalService _definitionService;

    public ReportLoadingService(IReportDefinitionRetrievalService definitionService)
    {
        _definitionService = definitionService;
    }

    public IReport GetReport(string reportName)
    {
        var reportDefinition = definitionService.GetDefinition(reportName);
        return GenerateReportFromDefinition(reportDefinition);
    }

    private IReport GenerateReportFromDefinition(string definition)
    {
        // Logic to construct an IReport implementation
    }

}

IReportDefinitionRetrievalService的实时实现将访问数据库并返回XML。现在,您的ReportLoadingService有责任填充IReport个实例,而另一个服务则负责实际获取报告定义。

对于单元测试,您可以创建IReportDefinitionRetrievalService的模拟,它可以执行您想要的任何操作(例如,在字典中查找定义)。看看Moq是否有一个好的模拟框架。它可以让你做这样的事情:

[Test]
public void GetReportUsesDefinitionService()
{
    var mockDefinitionService = new Mock<IReportDefinitionRetrievalService>();
    mockDefinitionService.Setup(s => s.GetDefinition("MyReportName")).Returns("MyReportDefinition");

    var loadingService = new ReportLoadingService(mockDefinitionService.Object);

    var reportInstance = loadingService.GetReport("MyReportName");

    // Check reportInstance for fields etc

    // Check the definition service was used to load the definition
    mockDefinitionService.Verify(s => s.GetDefinition("MyReportName"), Times.Once());
}

答案 2 :(得分:0)

  

1.工厂应该存储对象的集合,还是应该只负责从定义中创建对象,   将方法更改为IReport GetReport(string reportDefinition)

以上都不是。工厂是具体的实现选择器。它是作为静态方法实现的,它根据输入实例化具体对象。您已经完成了使用接口和名称正确解耦的工作。停在那里。

static IReport GetReport(string reportName)

IReport concreteReport = Factory.GetReport("myReportName")

通过单元测试对工厂进行测试非常简单。您可以创建一个报告名称数组,并通过反射或一些任意已知的断言检查IReport是否是正确的实现。依赖注入是一个完全不同的主题。