面向对象设计 - 通过接口暴露实现细节

5

我有一个类,保存了一些详细信息于大型数据结构中,在该类中可以使用算法对其进行计算,并且还有一些验证数据结构输入的方法。 但是我希望能够将该数据结构返回,以便视图模型可以将其转换为各种输出形式(字符串/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,它将构建输出而不是运行计算。然而,除了具体的类构造函数之外,我不知道如何做到这一点。

使用抽象类和虚方法。抽象类也是接口 - keenthinker
2个回答

2
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
}

主要思想是使用领域特定的界面来替代通用集合,这样你代码的读者和实现者就可以知道内容而不必查阅文档。
你甚至可以更进一步,用内部复杂结构的 IDictionary 替换为 IProductPeriods 的 IDictionary 实现。如果你想让暴露给用户的 IYearlyPeriods 不可变,但自己又想进行修改,你可以创建一个可变实现,并将其保留在实现类的 internal 中。

数据结构将产品名称(字符串)映射到一些年度数据(第二个IDictionary)。这反过来将一年(int)映射到一定数量的期间(set)。期间本身只是一个虚拟类,具有属性:期间编号(int)和金额(double)。 - oskarm

0
我建议将保持私有,并在接口中提供一个简单的。
在您的情况下,这可以是一个自定义DTO,隐藏所有IDictionary>的复杂性,这已经相当复杂,而且可能很容易随着您需要实现新功能而发生改变。
这可能类似于:
class ExposedPeriod
{
      public int PeriodIdentifier { get; set; }
      public IEnumerable<IPeriod> Periods { get; set; }
}

ExposedPeriodPeriodIdentifier可能需要更好的名称。可以在您的领域词汇中找到好的命名。


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