我应该避免在Scala中定义'object'吗?

3

我认为Scala中的“对象”与Java中的“单例模式”非常相似,而这种设计实践并不被认为是好的。对我来说,单例模式就像另一种定义全局变量的方式,这是不好的。虽然我写过一些像这样的Scala代码,因为它很容易且有效,但代码看起来很丑陋:

object HttpServer { // I'm the only HttpServer instance in this program.
  var someGlobalState: State
  def run() {
    // do something
  }
}

我试图避免这样做。什么时候定义Scala对象会更好?


可能是为什么单例对象更面向对象?的重复问题。 - kiritsuku
1
变量someGlobalState构造了你的问题。Scala对象不意味着全局状态或任何状态。 - pedrofurla
2
经验法则:对象不应该有任何可变状态。(有时可能会被打破,但这些情况应该持怀疑态度。) - Madoc
3个回答

10

不。许多 Scala 库都严重依赖于对象。

单例模式的主要目标是只能存在一个对象实例,同样适用于 Object。

你可能会误用它作为全局变量,但这并不是重点。

例如,Object 是工厂方法或替代模块保存函数的绝佳位置。


8

为什么您认为只有全局变量是必需的呢?全局值和方法也非常有用。这是在Scala中使用object的主要方式。

object NumericConstant {
  val Pi = 3.1415926535897932385   // I probably will not change....
}

object NumericFunctions {
  def squared(x: Double) = x*x     // This is probably always what we mean...
}

现在,你需要小心使用全局变量,如果你想要的话,可以在对象中实现它们。然后你需要弄清楚你是否粗心(注意:将类的同一个实例传递给程序中每个类和方法是同样有问题的)或者你所做的事情是否最好由一个单一的全局值来反映。
这里有一个非常糟糕的想法:
object UserCache {
  var newPasswordField: String = "foo bar"
}

两个用户同时更改密码,可能会导致一些不满意的用户。

另一方面,

object UserIDProvider {
  private[this] var maxID = 1
  def getNewID() = this.synchronized {
    var id = maxID
    maxID += 1
    id
  }
}

如果你不像这样做,那么你将会有一些不满意的用户。(当然,你真的需要在启动时读取一些关于用户ID号的磁盘状态......或者把所有这些东西保存在数据库中......但是你明白我的意思。)


1
我认为在你的示例中拥有像UserIDProvider这样的对象并不是一个好的方法。它会将此对象暴露给任何人。您可以在代码的任何地方操纵此对象中维护的全局状态。有时我很懒,不想太费心思考谁需要访问此对象,我只会将它们定义为单例。稍后我可以随时访问它们而不更改任何内容。但我相信将对象范围限制为尽可能小是一个好的做法。 - woodings
1
@woodings - 这取决于应用程序的范围。如果它真的关心新用户ID——也许是一个新的账户服务器——那么全局就可以了。如果它只是一个庞大系统中的一小部分,那么你可能不想让它在任何地方都可见。然而,解决问题的正确方法是——如果这确实是一个单一的全局问题——在单例上使用访问限制。如果你试图通过只实例化一个对象来模仿它,你可能会犯一个错误并创建两个对象。这样编译器就会为你处理它。所以,private[whatever] object 它。 - Rex Kerr

4

全局变量并不是本质上的坏事。你只需要理解何时使用它们才是合适的。同样道理,object 也不是本质上的坏事。例如:

object HelloWorld {
  def main(args:Array[String]){
     println("Hello World")
  }
}

不想详细讨论这个话题,我喜欢这样想:“我是因为最好反映现实情况才要'只有一个'东西吗?还是这只是一种懒惰的捷径来让事情'正常运作'?"

不要盲目而广泛地应用“单例模式是不好的”规则。有很多情况下,“只有一个”东西是有意义的。在您的特定情况下,我需要更多背景信息才能给出更具体的建议。


是的,你必须小心测试。有很多地方全局变量可能会让你陷入麻烦。对于相对初学者来说,即使没有单例模式,创建一个测试噩梦也是相当容易的(可以说)。这需要经验积累。 - Larsenal

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