得墨meter耳定律是有趣的编程原理。 这是我所知道的唯一一个近似数学的定义:
对象O的任何方法m只能调用以下类型的对象的方法:
- O本身
- m的参数
- 在m内创建/实例化的任何对象
- O的直接组成对象
- 全局变量,可由O访问,范围为m
如果您没有意识到,此列表不允许您调用任何允许的方法返回的任何方法。 它本质上禁止链接方法调用。 我会稍作一些例外。
为什么?
得墨meter耳定律的目的是什么? 它试图防止什么? Demeter定律旨在减少耦合并增加封装。 通过遵循迪米特法则,你让这个类Ø并不需要知道的比什么M做其他的米源东西。 为了减少与m的耦合, O除了m的存在外,不应该对m的返回有任何了解。
我可能没有最好的解释。 走出去看看别人如何解释这不是一个坏主意。 这篇文章的真正目的是描述遵守法律的方式,一种反模式以及一些例外情况。
反模式
我们将从反模式开始。 我称其为“分层私有方法”,这是我第一次想到“绕过Demeter法则”,但我很快发现,尽管从技术上讲它遵循所给出的规则,但并未遵循该法则的原因。
反模式就是这样。 假设您最初有以下代码要修改以遵循Demeter:
public void m(Parameter parameter)
{
ReturnValue result = parameter.method();
result.method();
}
要解决此问题,请将其更改为:
public void m(Parameter parameter)
{
otherMethod(parameter.method());
}
private void otherMethod(ReturnValue rv)
{
rv.method();
}
如您所见,每种方法仍然遵循法律,但是类仍然是耦合的。 每当您从内部调用O的另一个方法时,都应考虑该调用方法中的代码是m的一部分,以便仍然遵循Demeter定律的精神。
层数
那么,您应该怎么做才能修复代码? 在这种情况下,您可以将私有方法调用转换为对新类的调用:
public class NewClass
{
public void extractedMethod(ReturnValue rv)
{
rv.method();
}
}
所以m现在看起来像这样:
public void m(Parameter parameter)
{
NewClass helper = new NewClass();
helper.extractedMethod(parameter.method());
}
现在,这似乎是一个完全的浪费,创建一个新类,该类实际上是私有方法的替代品。 但是,实际上,有很多人认为每个私有方法都应该成为一个新类。 虽然我同意我们的私有方法确实显示出超越它们所包含的类范围的趋势。 每当您看到私有方法时,请考虑从中提取一个类。
但是,这是打电话给那个班级的最好方法吗? 到目前为止,您应该已经了解了依赖注入(如果不这样做,请进行查找;它非常简单,但是很重要)。 如果移到类的调用非常复杂,以至于需要进行自己的测试,则应以自己认为最合适的方式应用DI。 如果没有,请考虑一下。 但是,除非您希望有多个实现,否则可能没有充分的理由立即执行此操作。
例外1:数据结构
当m是数据结构上的方法或返回数据结构时,可以忽略Demeter定律。 数据结构是一类,其整体目的基于存储/表示数据。 这包括原始类型包装器,String,“ java bean”等。 可以理解的是,如果数据的定义发生变化,则数据的用户必须适应这种变化。 如果您调用的方法(违反了得墨meter耳法则)只是返回公共内部数据(数据本身可能是私有的,但使用公共获取程序),则没有充分的理由不允许这样做。
遵循以下准则,不可变对象也可能是违反法律的一部分:“错误”方法调用1)返回公共内部数据(如之前)或2)返回与自身类型相同的新对象。
数字2是安全的,因为方法调用等效于增加数字。 如果整数不具备使用运算符的能力,则假设对象是不可变的,则增量将与数字2( n = n.increment()
)完全相同。
例外2:构建器模式和其他Fluent API
当API设计为流利的(因此通常是链接的)时,没有充分的理由要成为严格的Demeter追随者而失去可读性。 例如,如果您不允许自己链接方法,那么Java 8的Stream
API将一文不值。
奥托罗
希望您现在对Demeter定律的目的和用法有更好的了解。 现在开始编程! 玩得开心!
翻译自: https://www.javacodegeeks.com/2015/03/law-of-demeter-and-how-to-work-with-it.html