替代在构造函数中执行大量计算 - scala

Rus*_*ell 8 constructor scala

我正在学习新项目的scala,尽可能地追求不变性和功能性风格.

我正在创建的对象之一在其构造函数中接受了大量输入,然后重复应用大量计算来生成相关输出,这些输出作为字段存储在对象上.

虽然执行了计算并且它们的结果在ListBuffer内部添加到了可变的内容,但是关于该对象的所有其他内容都是不可变的 - 一旦创建,您就无法更改任何输入值并再次运行计算显然会产生相同的结果.

但是,在构造函数中进行如此多的计算似乎并不合适.我能看到的唯一方法是让计算值为vars并提供一个run执行计算的方法 - 但是这个方法可以被多次调用,这是没有意义的.

在scala构造函数中做很多事情真的很好吗?例如,没有对DB的调用,只是内部计算.或者有一些模式吗?

这是非常简单形式的基本思想:

class Foo(val x:Int, val y:Int, calculations:List[Calculation]) {
  val xHistory = new collection.mutable.ListBuffer[Int]()
  val yHistory = new collection.mutable.ListBuffer[Int]()

  calculations.map { calc => calc.perform(this) }.foreach { result => 
    xHistory += result.x 
    yHistory += result.y
  }
}
Run Code Online (Sandbox Code Playgroud)

基本上我希望输入包装在一个方便的Foo对象实例中,这样我就可以将它传递给各种计算策略(每个计算策略可能需要不同的输入组合).

zig*_*tar 6

在构造函数内部工作

通常我在构造函数中做昂贵的东西.但请注意,评论中提到构造函数代码可能不太优化(在此处插入Java实现,这是正确的).如果您有多线程应用程序,也请阅读下一段.

延迟初始化

我不知道在构造函数中做大量工作可能有什么问题.

正如评论中所指出的,构造函数内部运行的代码可能存在问题.因此DelayedInit,Scala 2.8.0中引入了这一特性.例如,在使用Swing GUI元素时会出现此类问题.

DelayedInit特征提供了另一种工具来自定义类和对象的初始化序列.如果类或对象继承自此特征,则其所有初始化代码都打包在一个闭包中,并作为参数转发给名为delayedInit的方法,该方法在特征DelayedInit中定义为抽象方法.

因此,在执行初始化代码时,delayedInit的实现具有完全的自由度.例如,Scala的新App特征将所有初始化序列存储在内部缓冲区中,并在调用对象的main方法时执行它们.

懒惰的构造

要以不同的方式延迟计算,可以使用以下方法,这也解决了并发问题:

  • 您可以使用lazy val成员,这些成员将在首次请求时进行计算.
  • 如果计算一系列昂贵的对象,则可能需要使用"惰性"数据结构Stream.这就像List只按需计算下一个元素.因此,在某个时间点,仅Stream计算了已经访问过的那部分的初始部分.

使用说明 lazy

您可能想要做的另一个考虑因素是计算值是否被使用.如果可能不需要它们,那么使用我描述的惰性方法就是要走的路.另一方面,如果你肯定访问这些昂贵的成员,我认为在构造函数中进行计算没有任何问题,并且使用惰性成员可能会增加不必要的计算开销.

关于OP示例的注释

在构造函数中做危险的事情是不对的; 比如让部分构造的对象的引用转义构造函数(通过this-reference).OP内部的例子就是这样做的calc.perform(this).以下建议无法修复此"潜在错误".