像这样的代码是“火车残骸”吗(违反了得墨meter耳定律)?


23

浏览我编写的一些代码后,我遇到了以下使我思考的结构。乍一看,它似乎足够干净。是的,在实际代码中,该getLocation()方法的名称稍微更具体一些,可以更好地准确描述其到达的位置。

service.setLocation(this.configuration.getLocation().toString());

在这种情况下,service是在方法中声明的已知类型的实例变量。this.configuration从传递给类构造函数开始,它是实现特定接口(强制使用公共getLocation()方法)的类的实例。因此,表达式的返回类型this.configuration.getLocation()是已知的。特别是在这种情况下,它是一个java.net.URL,而service.setLocation()想要一个String。由于这两种类型的字符串和URL不直接兼容,一些类型的转换的需要,以适应方形挂在圆孔。

但是,根据Clean Code中引用的Demeter定律,类C中的方法f应该仅调用C上的方法,由f创建或作为f的参数传递的对象以及C的实例变量中包含的对象。超出此范围的任何内容(除非是我在上面特定情况下的最终结果,除非您考虑由于方法调用本身而创建的临时对象,在这种情况下,整个法律似乎都没有意义)。toString()

考虑到列出的限制条件,为什么不鼓励或什至不允许上述呼叫,所以有合理的理由吗?还是我只是太挑剔了?

如果我要实现一种方法URLToString(),该方法只是调用作为参数传递给它toString()URL对象(例如,由返回getLocation()),然后返回结果,则可以将getLocation()调用包装在其中以实现完全相同的结果;有效地,我只是将转换向前移了一步。那会以某种方式使其可以接受吗?(在我看来,从直觉上看,这两种方式都不应该有任何区别,因为所做的只是稍微移动一下东西。但是,按照所引用的《得墨meter耳定律》的信,这是可以接受的,因为我然后直接在函数的参数上进行操作。)

如果这比调用toString()标准类型更具有异国情调,会有所不同吗?

在回答时,请切记,更改service变量所属类型的行为或API 是不切实际的。同样,为了争辩,我们说改变返回类型getLocation()也是不切实际的。

Answers:


34

这里的问题的签名setLocation。它是字符串类型的

详细说明:为什么会这样String?A String代表任何类型的文本数据。它可能不是有效位置,而是任何东西。

实际上,这带来了一个问题:什么是位置?如何知道没有寻找到你的代码?如果URL不是,我将对该方法的预期了解更多。
也许将它作为一个自定义类会更有意义Location。好的,我一开始不知道那是什么,但是在某个时候(可能在编写之前,this.configuration.getLocation()我将花一分钟的时间弄清楚该方法返回的是什么)。
当然,在两种情况下,我都需要寻找其他地方来了解预期的结果。但是在后一种情况下,如果我了解a Location是什么,我可以使用您的API,在前一种情况下,如果我了解a String是什么(可以预期),我仍然不知道您的API期望什么。

在不太可能的情况下,位置是任何类型的文本数据,我都会将其重新解释为任何具有文本表示形式的数据。鉴于事实,那Object有一个toString方法,您可以使用它,尽管这需要您的代码客户有相当大的信心。

您还应该考虑一下,这就是您正在谈论的Java,它在设计上几乎没有什么功能。这就是迫使您toString在末尾实际调用的原因。
例如,如果您也使用静态类型的C#,则实际上可以通过定义隐式强制转换的行为来忽略该调用。
在像Objective-C这样的动态类型语言中,您实际上也不需要转换,因为只要值的行为像字符串一样,每个人都会很高兴。

有人可能会争辩说,对toStringJava的最后一次调用要少得多,实际上不是Java要求显式性所产生的噪音。您正在调用任何 Java对象都具有的方法,因此您实际上并未对有关“远程单位”的任何知识进行编码,因此不会违反“最少知识原理”。无论getLocation返回什么,都没有toString方法。

但是请不要使用字符串,除非它们确实是最自然的选择(或者除非您使用的是语言,否则甚至都没有枚举……)。


我倾向于同意您的意见,但是他已经说过他不能修改服务API。
2011年

1
@jhocking:他没有说他不能。他说这是不切实际的。我不同意。仅当此类解决方法是唯一可能的选择时,才尝试将最佳实践应用于可解决API设计缺陷的代码,才有意义。但是,此处直接设置API是最佳选择。消除缺陷总是比解决它们始终更好。
back2dos

无论如何,还是要+1,因为“这实际上不是一个电话,而是Java的显性需求所产生的噪音”
2011年

1
即使是“字符串输入”,我也会给+1。在我的代码中,我尝试传递尽可能表达意思/意图的类型,但是当使用第三方库和API时,有时您会因使用该API的作者所做出的决定而陷入困境。和IIRC,位置确实是“任何一种文本数据”,但对于实现我的工作,非URL位置是完全没有意义的。
CVn

1
至于更改API;这是计算机软件,因此几乎总是可以进行更改。(如果没有别的什么,您总是可以编写一个抽象层。)但是,有时这样做会涉及太多的短期和长期努力,因此是没有道理的。然后,完全有可能进行此类更改,但仍然不切实际。
CVn

21

得墨meter耳的法律是设计指南,而不是虔诚遵循的法律。

如果您认为自己的类已经足够解耦,而这行没有错,this.configuration.getLocation()尤其是如您所说,更改API的其他部分不切实际的话。

我很确定,只要您按时交付Demeter的定律,即使您将Demeter的定律分解成碎片,客户也将非常满意。这不是制造不良软件的借口,而是在开发软件时的务实提醒。


1
由于this.configuration是已知接口类型的实例变量,因此即使按照严格的解释,在该接口上定义的方法上调用该方法似乎也不错。是的,我知道这是一个准则,就像KISS,SOLID,YAGNI等一样。通用软件开发中几乎没有(法律意义上的)“法律”。
CVn

4
+1是务实的;-)
Treb

1
我不认为《痴呆者定律》在这种情况下甚至不适用-配置基本上是一个容器。在我曾经使用过的每个API中,查看容器内部都是正常和预期的行为。
罗伦·佩希特尔

2

我唯一不想到不编写这样的代码是,如果this.configuration.getLocation()返回null会怎样?这取决于您周围的代码和使用此代码的目标受众。但是正如Marco所说的那样-Demeter的定律是一个经验法则-可以遵循,但不要在不必要的情况下屈服。


如果this.configuration.getLocation()返回null,那么我们要么(a)最有可能永远不会走到那么远,要么(b)在此期间发生了灾难性的事情,在这种情况下,我希望代码失败。因此,尽管从总体上来说这绝对是一个有效的观点,但是在这种特殊情况下,可以肯定地说它不适用。同样,围绕所有这些以及更多的是一个异常处理程序,专门设计用于处理此类意外故障。
CVn

2

严格遵循Demeter定律意味着您应该在配置对象中实现以下方法:

function getLocationAsString() {
  return getLocation().toString();
}

但就我个人而言,我不会打扰,因为这是一个很小的情况,而且由于您无法更改,因此无论如何您的双手都被束缚了。编程规则是关于选择时应该做的事情,但是有时您别无选择。


1
此处的建议相同:c2.com/cgi/wiki?TrainWreck “创建一种表示所需行为并告诉客户该怎么做的方法。这遵循“告诉,不要问”的原则。”
heltonbiker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.