将命令式 Java 转换为功能性 Java(一个游戏)

我正在学习更多关于Java 8及其功能的知识,我想用它做更多的练习。例如,假设我有以下命令式代码,用于在屏幕边界周围缠绕一个圆圈:

if (circle.getPosition().getX() > width + circle.getRadius()){
    circle.getPosition().setX(-circle.getRadius());
}else if (circle.getPosition().getX() < -circle.getRadius()){
    circle.getPosition().setX(width + circle.getRadius());
}
if (circle.getPosition().getY() > height + circle.getRadius()){
    circle.getPosition().setY(-circle.getRadius());
}else if (circle.getPosition().getY() < -circle.getRadius()){
    circle.getPosition().setY(height + circle.getRadius());
}
  1. 我该如何尝试“功能化”它?也许是一些伪代码?在我看来,可变性和状态似乎是这个例子所固有的。
  2. 函数式编程不适合游戏开发吗?我喜欢这两者,所以我试图将它们结合起来。

答案 1

此示例中对可变性的要求没有任何固有的要求。命令式方法是通过应用改变现有圆圈状态的副作用来修改现有圆圈。

函数式方法是具有不可变的数据结构,并创建一个从第一个结构中获取数据并创建新结构的函数。在你的示例中,函数方法将使圆是不可变的,即没有setX()或setY()方法。

private Circle wrapCircleAroundBounds(Circle circle, double width, double height) {
    double newx = (circle.getPosition().getX() > width + circle.getRadius()) ? -circle.getRadius() : width + circle.getRadius()
    double newy = (circle.getPosition().getY() > height + circle.getRadius()) ? -circle.getRadius() : height + circle.getRadius()
    return new Circle(newx, newy)
}

使用Java8的功能特性,您可以想象将圆圈列表映射到包装的圆圈:

circles.stream().map(circ -> wrapCircleAroundBounds(circ, width, height))

命令式方法和函数式方法具有不同的优点,例如,由于不可变性,函数式方法具有不可变性,因此您应该能够更轻松地并行化此类代码。例如,一个人同样可以安全地写:

circles.parallelStream().map(circ -> wrapCircleAroundBounds(circ, width, height))

我不认为函数式编程一定不适合游戏开发,但是,尽管它已经完成,但它肯定不是一种标准方法,所以如果你使用函数式语言,你不会得到相同级别的库支持。

正如dfeuer在他的答案中指出的那样,Java的功能特性非常原始 - 你不支持代数数据类型,模式匹配等,这将使以函数式风格表达问题变得更加容易(至少一旦你习惯了这些习语)。我同意至少阅读一些关于Haskell的信息,它有一个很好的教程:http://learnyouahaskell.com/chapters 将是入门的好方法。与 Scala 不同,Scala 是一种非常多范式的语言,在学习新风格时,你不会有 OOP 功能可以依赖。


答案 2

对于你的第一点:你通过思考代码应该实现的目标来“功能化”你的例子。这就是,你有一个圆,并且想要根据某些条件计算另一个圆。但是由于某种原因,你的命令式教养让你认为输入圈和输出圈应该存储在相同的内存位置!

为了发挥功能,第一件事就是忘记记忆位置,拥抱价值观。以您想到的相同方式思考每种类型或其他数值类型。intjava.lang.Integer

例如,假设一些新手向您展示了一些这样的代码:

double x = 3.765;
sin(x);
System.out.println("The square root of x is " + x);

并抱怨这似乎不起作用。你会怎么想呢?sin

现在考虑一下:

Circle myCircle = ....;
wrapAroundBoundsOfScreen(myCircle);
System.out.println("The wrapped-around circle is now " + myCircle);

您将攀登函数式编程的第一步,而后一种代码对您来说似乎与前者一样荒谬。是的,这确实意味着不要使用您正在使用的命令式语言的某些功能,或者非常谨慎地使用它们。