OO设计——通过接口公开实现细节

本文关键字:实现 细节 设计 OO 接口 | 更新日期: 2023-09-27 18:12:33

我有一个类,它在一个大型数据结构中保存一些细节,接受一种算法对其执行一些计算,具有验证数据结构输入的方法。但是之后我想返回数据结构,这样它就可以被视图模型转换成各种输出形式(字符串/c#数据表/自定义文件输出)。

class MyProductsCollection {
    private IDictionary<string, IDictionary<int, ISet<Period>>> products;
    // ctors, verify input, add and run_algorithm methods
}

我知道你应该使用"依赖接口而不是实现"的设计原则,所以我想为类创建一个接口。

如何避免编写以下接口?原因是它将暴露实现细节并绑定任何其他具体实现以返回相同的表单。

interface IProductsCollection {
    IDictionary<string, IDictionary<int, ISet<IPeriod>>> GetData();
    // other methods
}

我如何能够轻松地迭代数据结构以形成不同种类的输出,而不像这样直接暴露它?

编辑:

由于该类在构造函数中接受IFunc<IDictionary<string, IDictionary<int, ISet<IPeriod>>>>来遍历数据结构并执行计算,因此我可以为它提供另一个IFunc,它将构造输出而不是运行计算。但是,除了具体的类构造函数,我不知道如何做到这一点。

OO设计——通过接口公开实现细节

IDictionary<string,IDictionary<int,ISet<Period>>>的结构确实非常可疑——当你看到字典的字典时,很有可能你错过了创建一个类来封装内部字典的机会。

在不太了解问题所在领域的情况下,我建议定义一个接口来封装内部字典。它看起来像是将一个数字与一组句点关联起来的东西,因此您可以这样定义一个接口:

interface IYearlyPeriods {
    bool HasPeriodsForYear(int year);
    ISet<Periond> GetPeriodsForYear(int year);
}

我不知道句号里有什么,所以你需要为接口选择一个特定于域的名称。

此外,您还可以包装下一级IDictionary:

interface IProductDataSource {
    IEnumerable<string> ProductNames { get; }
    IYearlyPeriods GetProductData(string productName);
}

现在你可以这样定义一个接口:

interface IProductsCollection {
    IProductDataSource GetDataSource();
    // other methods
}

主要思想是使用特定于领域的接口来代替泛型集合,这样代码的读者和实现者就可以在不参考文档的情况下了解其中的内容。

你可以更进一步,用IProductPeriods实现的IDictionary内部保持的复杂结构替换IDictionary。如果你想保持你暴露给用户的IYearlyPeriods不可变,但希望能够自己修改,你可以做一个可变的实现,并保持它的internal实现类。

我建议保持IDictionary私有,并在接口中提供一个简单的IEnumerable

在您的情况下,这可能是一个自定义的DTO,它隐藏了IDictionary<int, ISet<IPeriod>>的所有肮脏-这已经相当复杂,可以(可能)很容易地改变,因为您需要实现新的功能。

可以是这样的:

class ExposedPeriod
{
      public int PeriodIdentifier { get; set; }
      public IEnumerable<IPeriod> Periods { get; set; }
}

ExposedPeriodPeriodIdentifier可能需要更好的名字。好的名字可能在您的域词汇表中找到。