使用不可变性设计(在Scala中)

5
在像Scala这样的编程语言中强调不可变性(避免使用“var”),这是否意味着我的对象中“修改状态的方法”必须返回实例的副本(带有新状态)?
让我们考虑海龟。我想像这样移动我的海龟:
val turtle = new Turtle(0, 0, "north")
val turtle2 = turtle.turnLeft().forward(5).turnRight().backward(2)

在这里,turtle2不会指向Turtle的同一个实例(它们是两个独立的实例)。实际上,在那个移动序列中,创建了4个临时对象。例如,这就是我如何实现turnLeft方法:

def turnLeft {
  self.copy(orientation = self.orientation match {
    case "north" => "west"
    case "east" => "north"
    case "south" => "east"
    case "west" => "south"
  })
}

这是正确的设计方法吗?

如果是,那么它有多么高效或低效(每次方法调用都创建新对象)?如果不是,什么是正确的方法?在我的不可变性方面的理解中出了什么问题/缺失了什么(或者可能是函数式编程一般)?

提前感谢, Raka


我认为你的代码在不可变的角度看起来是正确的。就效率而言,我认为它肯定不会像单个可变预分配对象那样快。总体影响将取决于您想要多快和多频繁地移动海龟。 - Soumya Simanta
谢谢,我会注意的。 - Cokorda Raka
2个回答

9

创建许多短暂的对象是Scala的一个显著特征。如果您在具有足够大的堆和足够多的年轻代内存以容纳所有对象的JVM上运行它,则通常不会非常昂贵。

话虽如此,不可变性并不是一种信仰。常识应该占主导地位,并指导设计决策,当坚持“范例”变得过于繁琐时。


感谢您的回复。我不介意使用这种方法(创建短暂的对象)。只是...在我的“状态修改”方法上手动执行“self.copy(...)”看起来很麻烦。在Scala中有更好的方法吗(也许是方法的特殊注释)? - Cokorda Raka
嗯,“copy(foo=bar)”似乎并不比“foo=bar; return this”更加“繁琐”,是吧?还是我漏看了什么? - Dima

1

我认为,考虑是否将Turtle视为实体很重要。

如果我有一辆汽车并向右转动方向盘,汽车向右行驶(在最佳情况下 :P),然后我向左转动方向盘,它向左行驶,但它仍然是同一辆汽车。对于海龟也是如此。虽然不可变性通常使程序更易于理解,但在某些情况下,它可能不太直观。

想想看,右转和左转的海龟是否不同。当然,相等性检查可以被覆盖,因此新旧海龟(具有相同名称/ID)是否仅通过相等性或引用性也是相同的是一个理论上的差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接