设置不可变字段供以后使用



我的情况是,我需要在单例对象中设置某个字段,以便以后可以使用。

def register(importantField: String): Unit = {
Factory.setField(importantField)
}

其中Factory是:

object Factory {
var field: Option[String] = None
def setField(importantField: String): Unit = {
field = Option(importantField)
}
def functionThatWillBeCalledLater: Unit = {
// do something with member "field"
}
}

但我真的很想避免使用var.有没有某种惯用的方法可以做到这一点?

不要这样做。全球工厂除了不雅和破坏参考透明度之外,也是单元测试的噩梦。你应该真正考虑以某种方式设计你的代码,你可以将所有"重要字段"保留在你需要的地方。

诀窍是,而不是类似的东西

class Foo {
def doFoo() = Factory.bar
}
Factory.register(param)
new Foo().doFoo()

你必须写

class Foo { 
def doFoo(factory: Factory) = factory.bar
}
new Foo().doFoo(new Factory(param)

或者也许

class Foo(factory: Factory) {
def doFoo() = factory.bar
}
new Foo(new Factory(param)).doFoo

相比之下,将所有内容放入全局状态对象并从任何地方访问它似乎有点乏味......但这只是只要该函数的所有用法任何地方始终使用相同的参数值(在这种情况下,最好首先是一个常量)(也许更重要的是)直到您开始考虑编写单元测试来测试使用您的Factory的地方。你不能嘲笑一个对象...那么,你会怎么做?

Singleton 对象必须是线程安全的 - 由于对象是静态的,因此不能保证只有一个线程会访问它,因此您不使用var的调用是完全合理的。所有val都是最终的,并保证是线程安全的,但我们不能对vars说同样的话(好吧,如果我们谈论的是惯用语,可变状态通常是不受欢迎的)。

您可以为私有 var 实现自己的 getter 和 setter(使用正确的同步),也可以为此目的使用scala.concurrent.Promise-Promise只能完成一次,并且保证线程安全。

import scala.concurrent.Promise
object Factory {
private val _field: Promise[String] = Promise()

// the notation below allows you to set it via `Factory.field = "a"`
def field_=(value: String): Boolean = _field.trySuccess(value)

def field: Option[String] = _field.future.value.flatMap(_.toOption)
}

也就是说,使用可变基线条件处理工厂的最惯用的 Scala 方法是创建一个将所有变量设置为所需值的工厂。object本质上只是一个val,所以你可以用你需要的所有参数实例化Factory,而不是一个实际的单例对象,一旦它们可用 - 然后到处使用它。

最新更新