避免输出参数(没有副作用)

Mav*_*ode 6 java coding-style side-effects parameter-passing

我正在阅读 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
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们现在调用以下函数:

player1.fight(player2);
Run Code Online (Sandbox Code Playgroud)

这将改变player1的状态以及player2的状态。在大多数情况下,罗伯特·C·马丁(Robert C. Martin)不鼓励这样做。但实际上,我经常看到这种模式。大多数 Java 程序确实存在突变,对象的更改超出了它们创建的范围。

如果我们创建一场改变两个玩家的战斗,情况会更糟,因为现在另一个对象在其方法内改变了两个参数:

battle.fight(player1, player2)
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么吗?什么时候可以改变方法内的参数(在Java中)?我前段时间问过一个非常类似的问题(Is mutating object-parameters in a method(in Java) a bad Practice?)。

cam*_*024 4

归根结底,这些规则的存在是为了消除意外行为,让程序员的生活变得更轻松。它们帮助人类推理程序。因此,关键是要确保您可以查看一行代码并理解它的作用。

如果我,作为一名程序员,看到这一行:

player1.fight(player2);
Run Code Online (Sandbox Code Playgroud)

我希望player1player2都会以某种方式发生突变。这也适用于Arrays.sort(array);作为语言一部分的 。

但是,如果在您的isAlive()函数中,您执行了一些突变(可能会杀死健康状况低于零的玩家),那么在我看来,这是不好的做法,因为我作为程序员,期望调用一个函数isFoo()getFoo()简单地返回有关没有任何突变的对象。

归根结底,这些准则可以让您在一年后回来查看代码时变得更轻松,因此,如果您有疑问,请问问自己,您对具有相同签名的方法有何期望去做(即我期望public void setAge(int age);做什么?它能达到我的期望吗?)

聚苯乙烯

有些语言(例如 Haskell)根本不允许任何突变。排除 IO 和随机性,函数影响程序的唯一方式是通过它的返回值。如果您想了解如何完成“无副作用编程”,那么值得一试。