69

墨忒耳法则表明你应该只与你直接知道的物体交谈。也就是说,不要执行方法链接来与其他对象对话。当您这样做时,您正在与中间对象建立不正确的链接,将您的代码不恰当地耦合到其他代码。

那很糟。

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

那挺好的。

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

那很糟。

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

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

4

6 回答 6

49

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

“内聚的概念也来自结构化设计。简单地说,内聚衡量的是单个模块的元素之间的连接程度(对于面向对象的设计,是单个类或对象)。最不理想的内聚形式是巧合内聚,其中完全不相关的抽象被放入同一个类或模块中。例如,考虑一个由狗和航天器的抽象组成的类,它们的行为是完全不相关的。内聚最理想的形式是功能内聚,其中元素“

Subsitute Dog with Customer in the above and it might be a bit clearer. So the goal is really just to aim for functional cohesion and to move away from coincidental cohesion as much as possible. Depending on your abstractions, this may be simple or could require some refactoring.

Note cohesion applies just as much to a "module" than to a single class, ie a group of classes working together. So in this case the Customer and Order classes still have decent cohesion because they have this strong relationshhip, customers create orders, orders belong to customers.

Martin Fowler says he'd be more comfortable calling it the "Suggestion of Demeter" (see the article Mocks aren't stubs):

"Mockist testers do talk more about avoiding 'train wrecks' - method chains of style of getThis().getThat().getTheOther(). Avoiding method chains is also known as following the Law of Demeter. While method chains are a smell, the opposite problem of middle men objects bloated with forwarding methods is also a smell. (I've always felt I'd be more comfortable with the Law of Demeter if it were called the Suggestion of Demeter .)"

That sums up nicely where I'm coming from: it is perfectly acceptable and often necessary to have a lower level of cohesion than the strict adherence to the "law" might require. Avoid coincidental cohesion and aim for functional cohesion, but don't get hung up on tweaking where needed to fit in more naturally with your design abstraction.

于 2008-10-03T05:47:55.263 回答
21

如果你违反了得墨忒耳法则

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

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

int price = customer.getOrderPrice();

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

于 2008-10-02T15:53:01.760 回答
6

我想你可能误解了凝聚力的含义。一个类按照其他几个类来实现,其内聚度不一定低,只要代表一个明确的概念,有明确的目的即可。例如,您可能有一个class Person,它是根据班级Date(出生日期)Address、 和Education(该人上过的学校的列表)来实现的。您可以提供Person用于获取出生年份、上一所学校或他居住的州的包装,以避免暴露在Person其他类方面实施的事实。这会减少耦合,但Person不会降低内聚性。

于 2008-10-02T16:00:43.660 回答
4

这是一个灰色地带。这些原则旨在帮助您完成工作,如果您发现您正在为他们工作(即他们妨碍您和/或您发现它使您的代码复杂化),那么您太难遵守并且您需要退缩。

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

于 2008-10-02T16:01:42.597 回答
1

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

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

在多级类依赖的情况下要遵守得墨忒耳定律,你只需要在每一级应用聚合/组合和良好的封装。

换句话说,每个类对其他类都有一个或多个依赖项,但是这些只是对被引用类的依赖,而不是对从属性/方法返回的任何对象的依赖。

于 2008-10-02T16:00:17.110 回答
0

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

于 2008-10-02T15:45:17.693 回答