我的情况是,我需要在单例对象中设置某个字段,以便以后可以使用。
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
都是最终的,并保证是线程安全的,但我们不能对var
s说同样的话(好吧,如果我们谈论的是惯用语,可变状态通常是不受欢迎的)。
您可以为私有 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
,而不是一个实际的单例对象,一旦它们可用 - 然后到处使用它。