“深度合成层次结构”也不难吗?


19

抱歉,“合成层次结构”不是问题,但我将在问题中解释我的意思。

没有任何OO程序员没有遇到过“保持继承层次结构平坦”或“优先考虑继承而不是继承”等变体。但是,深层次的组合层次结构似乎也存在问题。

假设我们需要一组详细说明实验结果的报告:

class Model {
    // ... interface

    Array<Result> m_results;
}

每个结果都有某些属性。其中包括实验时间以及实验各个阶段的一些元数据:

enum Stage {
    Pre = 1,
    Post
};

class Result {
    // ... interface

    Epoch m_epoch;
    Map<Stage, ExperimentModules> m_modules; 
}

好,很好。现在,每个实验模块都有一个描述实验结果的字符串,以及对实验样本集的引用的集合:

class ExperimentalModules {
    // ... interface

    String m_reportText;
    Array<Sample> m_entities;
}

然后每个样本都有...好,您得到了图片。

问题是,如果我要在应用程序域中对对象建模,这似乎是很自然的选择,但是最终,a Result只是一个愚蠢的数据容器!为它创建大量的类似乎并不值得。

假设上面显示的数据结构和类正确地对应用程序域中的关系建模,是否有更好的方法可以对这种“结果”进行建模,而无需求助于深层次的构成层次结构?是否有任何外部环境可以帮助您确定这种设计是否是好的?


不了解外部上下文部分。
图兰斯·科尔多瓦

@TulainsCórdova我想在这里问的是:“在某些情况下,深层次的构成是一个很好的解决方案”?
AwesomeSauce

我添加了一个答案。
图兰斯·科尔多瓦

5
对,就是这样。人们试图通过用组合替换继承来解决的问题并没有因为您将一种功能聚合策略改为另一种而神奇地消失了。它们都是管理复杂性的解决方案,而且复杂性仍然存在,必须以某种方式进行处理。
梅森惠勒

3
我认为深度数据模型没有任何问题。但是我也看不到如何用继承建模,因为它在每一层都是1:N的关系。
usr

Answers:


17

这就是Demeter /最小知识原理所要解决的问题。使用者Model不必一定要了解ExperimentalModules'界面才能完成工作。

普遍的问题是,当一个类继承自另一个类型或具有某个类型的公共字段,或者当一个方法将一个类型作为参数或返回一个类型时,所有这些类型的接口都将成为我类的有效接口的一部分。问题就变成了:我所依赖的这些类型:是在全班学习吗?还是只是我不小心暴露的实施细节?因为如果它们是实现细节,我只是公开了它们,并允许班级的用户依赖它们-我的客户现在与我的实现细节紧密相关。

因此,如果我想提高凝聚力,可以通过编写更多有用的方法来限制这种耦合,而不是要求用户进入我的私有结构。在这里,我们可能会提供搜索或过滤结果的方法。这使我的班级更容易重构,因为我现在可以更改内部表示形式而无需中断我的界面。

但这不是没有代价的-我的公开类的接口现在变得膨胀起来,而这些操作并不总是需要的。这违反了接口隔离原则:我不应该强迫用户依赖比他们实际使用的更多的操作。这就是设计很重要的地方:设计是要确定哪种原则在您的上下文中更重要,并找到合适的折衷方案。

当设计一个必须在多个版本之间保持源兼容性的库时,我倾向于更谨慎地遵循知识最少的原则。如果我的库在内部使用另一个库,则永远不会向用户公开该库定义的任何内容。如果需要从内部库公开接口,则可以创建一个简单的包装器对象。诸如桥接模式或立面模式之类的设计模式可以在这里提供帮助。

但是,如果我的接口仅在内部使用,或者如果我要公开的接口是非常基础的(标准库的一部分,或者是如此基础的库,以至于根本无法更改它),那么隐藏起来可能就没用了这些接口。像往常一样,没有一个适合所有人的答案。


您提到了我的主要关切;我必须深入实现的想法,以便在更高的抽象水平上做有用的事情。非常有用的讨论,谢谢。
AwesomeSauce

7

“深度合成层次结构”也不难吗?

当然。该规则实际上应该说“保持所有层次尽可能平坦”。

但是,深层组合层次结构不像深层继承层次结构那样脆弱,并且可以灵活地进行更改。凭直觉,这不足为奇。家庭层次结构是相同的。您与亲戚的关系在遗传学中是固定的;您与朋友和配偶的关系不是。

您的示例并没有使我成为“深度层次结构”。表单是从矩形继承的,而矩形是从x和y坐标继承的一组点继承的,而我什至还不了解。


4

解释爱因斯坦:

保持所有层次结构尽可能平坦,但不要更平坦

这意味着,如果您正在建模的问题是那样的,那么您就不必对按原样建模感到烦恼。

如果应用程序只是一个巨大的电子表格,那么您就不必按原样建模合成层次结构。否则做。我不认为这件事是无限深的。如果填充此类集合的成本很高,则只需让返回集合的getter保持懒惰,例如要求将它们收集到将其拉到数据库的存储库中即可。懒惰的意思是,除非需要它们,否则不要填充它们。


1

是的,您可以像关系表一样对其进行建模

Result {
    Id
    ModelId
    Epoch
}

等等,这通常可以简化编程并简化到数据库的映射。但以失去对人际关系的严格编码为代价


0

我真的不知道如何用Java解决这个问题,因为Java围绕着一切都是对象的思想而构建。您不能通过用字典替换中间类来抑制中间类,因为数据Blob中的类型是异类的(例如时间戳+列表,或字符串+列表...)。用其字段替换容器类的替代方法(例如,将“ m_epoch”变量与“ m_modules”一起传递)是很糟糕的,因为它使隐式对象(没有其他对象就不能拥有一个)效益。

在我的偏好语言(Python)中,如果没有特定的逻辑(基本获取和设置除外),则我的经验法则是不创建对象。但是考虑到您的限制,我认为您拥有的合成层次结构是最好的设计。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.