首页 > 代码库 > 1.4.2.1 阅读函数式程序
1.4.2.1 阅读函数式程序
1.4.2.1 阅读函数式程序
我们在清单 1.1 中看过一个示例,就是使用了不可变类型,我们的结论是,不可变类型使代码更具可读性。在本节,我们将考虑两段代码,可以用在我们函数式游戏中。
清单 1.8 中有两个示例,都涉及两个游戏角色(player 和 monster)。第一个示例说明怪物[1] [ 原文中的 AI,应该就是这个 1 ]如何移动一步,然后,判断玩家是否正处于危险之中,第二示例演示如何射击。
Listing 1.8 Code snippets form a functionalgame (C#)
var movedMonster = monster.PerformStep(); [1]
var inDanger = player.IsCloseTo(movedMonster); [2]
(...)
var hitMonster =monster.HitByShooting(gunShot); [3]
var hitPlayer =player.HitByShooting(gunShot); [4]
(...)
代码的第一部分,执行怪物走一步[1],得到怪物的新状态,然后,判断玩家是否接近计算后怪物的位置;
第二部分处理虚拟环境中的射击,代码创建一个值,表示更新后的怪物[3],另一个值,表示玩家新有状态[4]。
我们这个函数式游戏中的所有对象都是不可变的,所以,当调用对象的方法时,不能修改这个对象和其他对象。如果我们理解了这一点,那么,对前面的例子就能得到几点看法。在第一段,我们首先调用怪物的 PerformStep 方法[1],该方法返回一个新的怪物,我们把它指定给变量 movedMonster;在下一行[2],我们检查玩家是否靠近这个怪物,而因此处于危险之中。
我们可以看到第二行代码依赖于第一个。如果我们改变这两行代码的顺序,程序不能编译,因为 movedMonster 没有在第一行中声明。
如果以命令式风格实现,该方法通常不会返回任何结果,它只会修改怪物对象的状态。在这种情况下,我们可以改变代码的顺序,代码可以编译,但它会更改了程序的含义,程序的行为不正确。
当游戏中射击发生时,会修改健康值,第二段的两行代码,用更新后的属性创建了新怪物[3]和新玩家[4]对象。这两行代码是独立的,因此,我们可以改变其顺序。那么,这个操作会改变程序的意思吗?看来它不应该,所有对象都是不可变的,就不会改变。令人惊讶的是,在命令式版本中,如果 gunShot 是可变的,就可能改变其意思。这些对象中的第一个可能会改变 gunShot 的一些属性,其行为是取决于两个语句的顺序。
清单1.8 相当简单,但它已表明不可变性能够消除很多可能的困难。在下一节,我们将看另一个重要的示例,但现在我们了解一下在这本书后面的一些内容。
重构和单元测试
如你所知,不可变性有助于我们理解程序做什么,因此,有助于重构代码。另一有意义的函数式重构正在改变代码的执行,程序的代码既可能在第一次到达时运行,也可能会延迟,在需要结果时才执行。不可变性对 F# 程序的开发方式非常重要,同样也使 C# 中的重构变得更容易。我们将在第十一章讨论重构。
不可变性的另一个优势体现在为函数式程序创建单元测试。因为不可变性,方法能做的唯一的事情就是返回结果,因此,我们唯一要测试的是,针对给定的参数,方法是否返回正确的结果。这一主题也会在第十一章讨论。
我们在讨论函数式编程更有效时,曾提到过不可变性对于更容易地写并行程序,非常重要。在下一节,我们将作简要探讨这一主题,以及其他相关主题。
1.4.2.1 阅读函数式程序