为什么我们应该为这个配置类创建一个接口?

10

我对接口的理解是,它们定义了协议,实现接口的类遵循这个协议。在这种情况下,我们可以设计依赖于协议而不是具体实现的类。这有许多优点,例如降低耦合度、实现多态等。

现在我发现了一个使用接口的用法,我不太明白。这是来自 ASP.NET Core 文档中关于配置的一部分。特别地,在 页面 中,谈论了如何使用 ASP.NET Core 配置 MongoDB。

他们基本上定义了一个类和一个接口,如下:

namespace BooksApi.Models
{
    public class BookstoreDatabaseSettings : IBookstoreDatabaseSettings
    {
        public string BooksCollectionName { get; set; }
        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }
    }

    public interface IBookstoreDatabaseSettings
    {
        string BooksCollectionName { get; set; }
        string ConnectionString { get; set; }
        string DatabaseName { get; set; }
    }
}

然后他们将这个用法如下:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<BookstoreDatabaseSettings>(
        Configuration.GetSection(nameof(BookstoreDatabaseSettings)));

    services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
        sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);

    services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

我必须说我不明白。类型为BookstoreDatabaseSettings的对象旨在作为设置的数据传输对象(DTO)工作,它们根本不提供任何功能。因此,为什么要在中间引入一个接口呢?

我没有看到这里有任何用途需要使用其他实现。 我不明白为什么要尝试解耦,我根本没有看到任何多态性的用处,并且我真的没有理解重点所在。

那么,在处理ASP.NET Core中的设置时,为什么要使用接口而不是直接使用具体类(例如BookstoreDatabaseSettings)?


5
老实说,这可能是一位曾经在完整的.NET Framework中工作的开发人员留下的残留物。那时,配置设置存储在App.config文件中,定义自定义部分需要使用烦人的XML注释并继承基础类型。所以为了解决单元测试的问题,我们定义了接口。随着在.NET Core中使用POCO配置,你是正确的,现在基本上没有理由不使用类了。 - zaitsman
1
也可以使其更具有“未来性”。例如,也许您将来会添加一个方法GetSettings,它连接到外部数据库,您可能希望在单元测试中模拟它。 - NotFound
如果接口只有getter方法那将是合理的,但由于这不是情况,你可以完全省略它。 - Darjan Bogdan
1个回答

9
在一个应用程序的上下文中,你是正确的,这没有意义。然而,在某些情况下,可能会有用处。
这里发生的第一件事情是将BookstoreDatabaseSettings绑定到配置部分。从技术上讲,这就是所需的全部内容。然而,这使用了选项模式,这意味着您必须注入IOptions<BookstoreDatabaseSettings>IOptionsSnapshot<BookstoreDatabaseSettings>等,而不是直接使用BookstoreDatabaseSettings。这意味着对Microsoft.Extensions.Options的依赖性,这并不是一个坏事,但它确实是一个依赖项。
接下来的一行代码将接口绑定到实际的BookstoreDatabaseSettings实例,该实例从IOptions<BookstoreDatabaseSettings>中获取。换句话说,现在您可以简单地注入接口,而不是IOptions<BookstoreDatabaseSettings>。再次强调,这并不算什么大事,但它抽象了Microsoft.Extensions.Options的使用。值得一提的是,您可以注册BookstoreDatabaseSettings而不是接口,以获得相同的结果。然后,您将能够直接注入BookstoreDatabaseSettings,而不依赖于Microsoft.Extensions.Options
接口在这里为您提供的是抽象实际类的实现。您可以将接口单独放在一个库中,在项目之间共享,并将实际的类实现留给每个项目。如果您的共享库仅依赖于接口,则提供的实际实现将不重要。然而,您同样可以在项目之间共享类实现。这只是一个视角的问题。

潜在地说,我的意思是,除了实际编写引用代码的人之外,任何其他人给出的答案都不过是猜测。然而,我认为总的来说,问题更多的是为什么你可能会选择使用接口,这是一个更客观的问题。 - Chris Pratt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接