耦合,衔接与德米特定律
得墨忒耳定律表明,你只应该直接与你所知道的物体说话。 也就是说,不要执行方法链接来与其他对象交谈。 当你这样做的时候,你正在与中间对象build立不正当的联系,不恰当地把你的代码和其他代码联系起来。
那很糟。
解决办法是为你所知道的类本质上公开简单的包装,将责任委托给它与之有关系的对象。
那很好。
但是,这似乎导致class级凝聚力低下。 它不再仅仅是对它所做的事情负责,而且它也使代表从某种意义上说,通过复制相关对象的接口部分来使得代码更less凝聚力。
那很糟。
这是否真的会降低凝聚力? 是两个邪恶中较小的一个?
这是发展的灰色地带之一吗?你可以在这里辩论哪一方面,或者有强有力的原则性的方式来决定在哪里划界,以及你可以用什么标准来作出这个决定?
Grady Booch在“面向对象的分析和devise”中的作用
“凝聚力的概念也来自结构化devise,简单地说,凝聚力衡量单个模块元素之间的连通程度(以及面向对象的devise,单个类或对象)。最不可取的凝聚forms是巧合的凝聚力,其中完全不相关的抽象被投入到同一个类或模块中,例如,考虑一个由狗和宇宙飞船的抽象组成的类,它们的行为是非常不相关的,最理想的凝聚forms是函数凝聚力,一个类或一个模块一起工作,以提供一些有界的行为,因此,如果它的语义包含一只狗,整只狗,除了狗之外,其他类都是function性的。
在上面的“与顾客配对狗”中,它可能会更清晰一些。 所以目标实际上只是瞄准function的凝聚力,尽可能远离巧合的凝聚力。 根据您的抽象,这可能很简单,或可能需要重构。
注意凝聚力同样适用于“模块”而不是单个类,也就是一组类一起工作。 所以在这种情况下,客户和订单类仍然有良好的凝聚力,因为他们有这个强大的关系,客户创build订单,订单属于客户。
马丁福勒说,他会更舒服地称它为“德米特的build议”(见文章莫克不是存根 ):
“冒牌testing人员更多地谈论避免”火车残骸“ – getThis风格的方法链getThat()。getTheOther()避免方法链也被称为遵循Demeter法则,尽pipe方法链是一种气味,中间人的物体与转发方法相反的问题也是一种嗅觉(我一直觉得如果把它称为“ 得墨忒耳的build议 ”,我会更喜欢“得墨忒耳定律”)。
这很好地概括了我来自哪里:与严格遵守“法律”可能要求的相比,凝聚力低下是完全可以接受的,而且往往是必要的。 避免巧合的凝聚力和目标的function凝聚力,但不要挂在需要调整,以适应更自然地与您的devise抽象挂钩。
如果你违反了德米特法则
int price = customer.getOrder().getPrice();
解决scheme不是创build一个getOrderPrice()并将代码转换成
int price = customer.getOrderPrice();
而是要注意,这是一个代码气味,并作出相关的变化,希望既增加凝聚力,降低耦合。 不幸的是,这里并没有简单的重构总是适用的,但是你应该可以申请告诉别问
我想你可能误解了凝聚力的意思。 一个以其他几个阶级来实现的阶级,不一定具有低凝聚力,只要它代表一个清晰的概念,并且有一个明确的目标。 例如,您可能有一个class Person
,这是根据class级Date
(出生date), Address
和Education
(人去的学校名单)实施的。 你可以提供包装材料,以获得出生年份,最后一个学校或他所居住的州,以避免暴露Person
被实施在其他类别的事实。 这样可以减less耦合,但是这会使Person
凝聚力降低。
这是一个灰色地带。 这些校长是为了帮助你的工作,如果你发现你正在为他们工作(即他们正在为你的方式和/或你发现它使你的代码复杂化),那么你很难遵守,你需要退后。
让它为你工作,不为它工作。
我不知道这是否真的降低了凝聚力。
汇总/组合都是关于一个阶级利用其他阶级来满足它通过公开的方式公开的合同。 该类不需要复制相关对象的接口。 它实际上隐藏了关于来自方法调用者的这些聚集类的任何知识。
在多层次依赖的情况下,要遵守德米特法则,只需要在每个级别上应用聚合/合成和良好的封装。
换句话说,每个类对其他类都有一个或多个依赖关系,但是这些依赖关系只是对被引用的类的依赖关系,而不是从属性/方法返回的任何对象。
在耦合和凝聚力之间似乎存在权衡的情况下,我可能会问自己:“如果别人已经写了这个逻辑,而且我正在寻找一个错误,那么我先看哪里?”用这种方式编写代码。