我正在阅读 Robert C. Martin 的“干净代码”,但我无法完全理解第 44 到 45 页上的“没有副作用”和“输出参数”部分。
在“无副作用”部分中指出,传递给方法参数的更改被视为副作用,不应进行。
在“输出参数”部分,声明方法的参数不应改变。如果必须更改某些状态,则方法应该仅更改其拥有对象的状态。
我确实理解,如果指定此行为的方法是由不完全了解这一点的客户端调用的,则副作用和输出参数可能会导致令人困惑的行为和错误。此外,这在多线程环境中工作时也是有问题的。
但是,关于“干净的代码”这本书是基于 Java 示例编写的,我感到很困惑。
让我们看一个例子。
假设我们有一个玩家类:
public class Player {
private int healthPoints;
private boolean alive = true;
public Player(int healthPoints) {
if(healthPoints < 1) {
throw new IllegalArgumentException();
}
this.healthPoints = healthPoints;
}
public boolean isAlive() {
return this.alive;
}
public void fight(Player otherPlayer) {
//do some fighting which will change this instance
// but also the otherPlayer instance
}
}
如果我们现在调用以下函数:
player1.fight(player2);
这将改变 player1 的状态和 player2 的状态。
在大多数情况下,Robert C. Martin 不鼓励这样做。但实际上,我经常看到这种模式。大多数 Java 程序真正涉及突变,并且对象在它们被创建的范围之外被改变。
如果我们创建一场改变双方玩家的战斗,情况会更糟,因为现在另一个对象在其方法中改变了两个参数:
battle.fight(player1, player2)
我在这里想念什么吗?什么时候可以在方法中(在 Java 中)改变参数?前段时间我问了一个非常相似的问题( Is mutating object-parameters in a method(in Java) a bad practice? )。
最佳答案
归根结底,这些规则的存在是为了通过消除意外行为来使程序员的生活更轻松。它们帮助人类推理程序。因此,关键是确保您可以查看一行代码并了解它的作用。
如果我,作为一个程序员,看到这一行:
player1.fight(player2);
我希望两者都是
player1
和 player2
以某种方式发生变异。这也适用于 Arrays.sort(array);
,它是语言的一部分。但是,如果在您的
isAlive()
函数,你执行了一些突变(可能杀死健康低于零的玩家),在我看来,这是不好的做法,因为作为程序员,我期待一个名为 isFoo()
的函数。或 getFoo()
简单地返回有关对象的数据而没有任何变化。归根结底,当您在一年后回来查看您的代码时,这些指南将使您的生活更轻松,因此,如果您有疑问,请问问自己您对具有相同签名的方法有何期望做什么(即我期望
public void setAge(int age);
做什么?它是否按照我的期望做?)附言
某些语言(例如 Haskell)根本不允许任何突变。排除 IO 和随机性,函数影响程序的唯一方式是通过它的返回值。如果您想了解如何完成“无副作用的编程”,值得一试。
关于java - 避免输出参数(没有副作用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59801071/