德墨忒耳定律 表示你应该只与你直接了解的对象交谈。也就是说,不要执行方法链接来与其他对象通信。当您这样做时,您正在与中间对象建立不适当的链接,不恰当地 耦合 您的代码到其他代码。

那很糟。

解决方案是对于您所了解的类,本质上公开简单的包装器,将责任委托给与其有关系的对象。

那挺好的。

但是,这似乎导致班级的成绩较低 凝聚. 。它不再只是简单地负责它所做的事情,而且它还具有委托,在某种意义上,通过复制其相关对象的接口部分来降低代码的凝聚力。

那很糟。

这真的会降低凝聚力吗?这是两害相权取其轻吗?

这是发展的灰色地带之一,您可以在其中争论界限在哪里,或者是否有强有力的、有原则的方法来决定在哪里划定界限以及您可以使用什么标准来做出该决定?

有帮助吗?

解决方案

Grady Booch在“面向对象的分析和设计”中:

“凝聚力的想法也来自结构化设计。简单地说,凝聚力 衡量单个模块的元素之间的连接程度(和 用于面向对象的设计,单个类或对象)。最不可取的形式 凝聚力是巧合的凝聚力,其中完全不相关的抽象是 扔进同一个类或模块。例如,考虑一个包含的类 狗和航天器的抽象,其行为是非常不相关的。该 最理想的凝聚力形式是功能凝聚力,其中的元素是 一个类或模块都协同工作以提供一些有条理的行为。 因此,如果它的语义包含行为,那么类Dog在功能上是有凝聚力的 一只狗,整只狗,只有狗。“

在上面与顾客合作,可能会更清楚一点。因此,目标实际上只是为了实现功能凝聚力,并尽可能地摆脱巧合的凝聚力。根据您的抽象,这可能很简单,也可能需要进行一些重构。

注意内聚同样适用于“模块”。而不是单个班级,即一组一起工作的班级。所以在这种情况下,客户和订单类仍然具有良好的凝聚力,因为他们有这种强大的关系,客户创建订单,订单属于客户。

Martin Fowler说他会更喜欢称之为“Demeter的建议”。 (参见文章模拟不是存根):

“模拟测试者确实更多地谈论避免'火车残骸' - getThis()风格的方法链.getThat()。getTheOther()。避免方法链也被称为遵循得墨忒耳定律。虽然方法链是一种气味,但是使用转发方法膨胀的中间人对象的相反问题也是一种气味。 (我一直觉得如果它被称为得墨忒耳的建议,我会更熟悉得墨忒耳法则。)“

这总结了我来自哪里:完全可以接受并且通常必须具有较低水平的凝聚力,而不是严格遵守“法律”。可能需要。避免巧合的凝聚力,并注重功能凝聚力,但不要因为需要更加自然地适应你的设计抽象而不知所措。

其他提示

如果您违反了得墨忒尔法则

int price = customer.getOrder().getPrice();

解决方案不是创建 getOrderPrice() 并将代码转换为

int price = customer.getOrderPrice();

但要注意这是一个 代码气味 并进行相关更改,希望能够增加内聚性并降低耦合。不幸的是,这里没有始终适用的简单重构,但您可能应该应用 告诉不要问

我认为你可能误解了凝聚力的含义。根据其他几个类实现的类不一定具有低内聚性,只要它代表一个清晰的概念,并且具有明确的目的。例如,您可能有一个类Person ,它是根据类 Date (出生日期), Address 和<代码>教育(该人去过的学校名单)。您可以在 Person 中提供包装,以获取出生年份,该人去过的最后一所学校或他所居住的州,以避免暴露 Person 的事实。根据其他课程实施。这会减少耦合,但它会使 Person 具有同样的凝聚力。

它是一片灰色地带。 这些原则是为了帮助你的工作,如果你发现你正在为他们工作(即他们&#8217;妨碍你和/或你发现它使你的代码复杂化)那么你&#8217; re符合要求太高,你需要退缩。

让它为你工作,不要为它工作。

我不知道这是否会降低凝聚力。

聚合/组合都是关于利用其他类来满足其通过其公共方法公开的合同的类。 该类不需要复制它的相关对象的接口。它实际上隐藏了来自方法调用者的关于这些聚合类的任何知识。

在多级别依赖的情况下,要遵守Demeter的规律,你只需要在每个级别应用聚合/组合和良好的封装。

换句话说,每个类对其他类有一个或多个依赖关系,但是这些只依赖于引用的类,而不依赖于从本地/方法返回的任何对象。

在耦合和凝聚之间似乎存在权衡的情况下,我可能会问自己“如果其他人已经写过这个逻辑,而我正在寻找其中的错误,我会先看哪里?“,并以这种方式编写代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top