The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
时间耦合发生在顺序方法调用之间,当它们必须保持特定顺序时。这在命令式编程中是不可避免的,但我们只需将这些静态过程转换为函数,就可以减少其负面影响。看看这个例子。
class Foo {
public List<String> names() {
List<String> list = new LinkedList();
Foo.append(list, "Jeff");
Foo.append(list, "Walter");
return list;
}
private static void append(
List<String> list, String item) {
list.add(item.toLowerCase());
}
}
你对此有什么看法?我认为“names ()”正在做的事情很明确——创建一个名字列表。为了避免重复,还有一个补充的procedure,“append ()”,它将项目转换为小写并将其添加到列表中。
这是一个糟糕的设计。
这是一个过程化设计,在方法“names ()”中的行之间存在时间耦合。
让我首先展示给你一个更好的(虽然不是最好的!)设计,然后我会尝试解释它的好处:
一个理想的with()
方法设计会创建一个新的List
实例,通过addAll(list)
填充它,然后再往里面add(item)
,最后返回。这样做完全符合不可变性,但是速度较慢。
那么,这种方法有什么问题呢:
看起来完全干净,不是吗?实例化一个列表,将两个项目添加到其中,然后返回它。是的,现在是干净的。因为我们记得append()
在做什么。几个月后,我们会回到这段代码,它将会变成这样:
现在是不是很清楚 append()
实际上是将 "Jeff"
添加到 list
中了?如果我删除那行,会发生什么?会影响最后一行返回的结果吗?我不知道。我需要检查一下 append()
方法的主体来确保。
还有,先返回 list
,然后再调用 append()
怎么样?这可能是我们的代码可能进行的“重构”操作。
首先,当list
还没有准备好时,我们会过早地返回它。但是有人告诉我,在return list
之前,这两个append()
调用必须发生吗?其次,我们改变了append()
调用的顺序。同样,有人告诉我按照特定的顺序调用它们很重要吗?
没有人。没有地方。这被称为时间耦合。
我们的代码行耦合在一起。它们必须保持特定的顺序,但是有关这个顺序的知识是隐藏的。很容易破坏顺序,而我们的编译器将无法捕捉到我们的错误。
相反,这种设计没有任何“顺序”:
它只是返回一个列表,该列表是通过对with()
方法进行几次调用构建而成的。它是一行代码,而不是四行。
如前所述,面向对象编程中的理想方法必须只有一条语句,而这条语句就是return
。
对于验证也是如此。例如,以下代码是不好的:
尽管这一个要好得多:
See the difference?
当然,最理想的方法是使用可组合的装饰器,而不是使用这些丑陋的静态方法。但如果由于某种原因不可能使用装饰器,就不要让这些静态方法看起来像过程。确保它们始终返回结果,并将结果作为进一步调用的参数。
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 14:50