该得墨忒耳定律指出你应该只给你关于直接了解对象说话.也就是说,不要执行方法链接与其他对象交谈.当您这样做时,您正在与中间对象建立不正确的链接,不恰当地将您的代码与其他代码耦合.
那很糟.
解决方案是针对您所知道的类,基本上公开简单的包装器,将责任委托给与其有关系的对象.
非常好.
但是,这似乎导致班级具有低凝聚力.它不再只是简单地对其所做的事情负责,但它也有代表在某种意义上,通过复制其相关对象的部分界面使代码更具凝聚力.
那很糟.
它真的会降低凝聚力吗?它是两个邪恶中的较小者吗?
这是发展的灰色区域之一,您可以在哪里讨论线路的位置,或者是否有强有力的,有原则的方式来决定在哪里划线,以及您可以使用哪些标准来做出决定?
Grady Booch在"面向对象分析与设计"中的应用:
"凝聚力的概念也来自结构化设计.简单地说,凝聚力测量单个模块的元素之间的连通程度(以及面向对象的设计,单个类或对象).最不希望的凝聚形式是巧合凝聚力,其中完全不相关的抽象被投入到同一个类或模块中.例如,考虑一个包含狗和航天器的抽象的类,其行为是非常不相关的.最理想的凝聚力形式是功能凝聚力,其中元素一个阶级或模块都在一起工作以提供一些有条理的行为.因此,如果它的语义包含狗,整只狗和狗的行为,那么类Dog在功能上具有凝聚力."
在上面与顾客一起划分狗,它可能会更清楚一点.因此,目标实际上只是为了实现功能凝聚力,并尽可能地摆脱巧合的凝聚力.根据您的抽象,这可能很简单,也可能需要进行一些重构.
注意凝聚力同样适用于"模块"而不是单个类,即一组一起工作的类.所以在这种情况下,客户和订单类仍然具有良好的凝聚力,因为他们有这种强大的关系,客户创建订单,订单属于客户.
Martin Fowler说他更习惯称之为"Demeter的建议"(参见文章Mocks不是存根)):
"模拟测试者确实更多地谈论避免'火车残骸' - getThat()风格的方法链.getThat().getTheOther().避免方法链也被称为遵循Demeter法则.虽然方法链是一种气味,使用转发方法膨胀的中间人对象的相反问题也是一种气味.(如果被称为Demeter的建议,我一直觉得我会更熟悉得墨忒耳法则.)"
这总结了我来自哪里:完全可以接受并且通常必须具有较低的凝聚力,而不是严格遵守"法律"所要求的.避免巧合的凝聚力,并注重功能凝聚力,但不要因为你的设计抽象更加自然地适应需要调整.
如果您违反了得墨忒耳法则
int price = customer.getOrder().getPrice();
解决方案不是创建getOrderPrice()并将代码转换为
int price = customer.getOrderPrice();
但请注意,这是一种代码气味,并进行相关的更改,希望增加内聚力和降低耦合.不幸的是,这里没有简单的重构总是适用,但你应该应用tell不要问
我想你可能误解了凝聚力的含义.根据其他几个类实现的类不一定具有低内聚性,只要它代表一个清晰的概念,并且具有明确的目的.例如,您可能有一个class Person
,按类别Date
(出生日期)Address
,和Education
(一个人去过的学校列表)实施.您可以提供包装,Person
以获得出生年份,该人去过的最后一所学校或他所居住的州,以避免暴露以Person
其他课程实施的事实.这会减少耦合,但它会产生Person
同样的凝聚力.