Scala对象和singleton的兴起



一般风格问题。

随着我越来越擅长编写函数代码,我的方法越来越多地变成了纯函数。我发现我的许多"类"(松散意义上的代码容器)正在变得无状态。因此,我将它们作为对象而不是类,因为不需要实例化它们。

现在在Java世界中,拥有一个充满"静态"方法的类似乎很奇怪,而且通常只用于"辅助"类,就像你在Guava和Commons-*等中看到的那样

所以我的问题是,在Scala的世界里,在"对象"而不是"类"中有很多逻辑是很正常的,还是有另一个首选的习惯用法。

正如您在标题中提到的,对象是单例类,而不是您在问题文本中提到的具有静态方法的类。

在java世界中,有一些东西使scala对象比静态And单例都好,所以在scala中使用它们是非常"正常"的。

首先,与静态方法不同,对象方法是多态的,因此可以很容易地将对象作为依赖项注入:

scala> trait Quack {def quack="quack"}
defined trait Quack
scala> class Duck extends Quack
defined class Duck
scala> object Quacker extends Quack {override def quack="QUAACK"}
defined module Quacker
// MakeItQuack expects something implementing Quack
scala> def MakeItQuack(q: Quack) = q.quack
MakeItQuack: (q: Quack)java.lang.String
// ...it can be a class
scala> MakeItQuack(new Duck)
res0: java.lang.String = quack
// ...or it can be an object
scala> MakeItQuack(Quacker)
res1: java.lang.String = QUAACK

这使得它们可以在没有紧耦合和不促进全局状态的情况下使用(这是通常归因于静态方法和单态的两个问题)。

还有一个事实是,他们去掉了所有的样板,这些样板使单身汉在java中看起来如此丑陋和独特。在我看来,这是一个经常被忽视的点,也是单身汉在java中如此不受欢迎的部分原因,即使他们是无状态的,并且没有被用作全局状态。

此外,您必须在所有java singleton中重复的样板给类两个责任:确保只有一个自己的实例,并做它应该做的任何事情。scala有一种声明性的方式来指定某个东西是singleton,这一事实使类和程序员免于打破单一责任原则。在scala中,你知道一个对象是一个单例,你可以推理它的作用。

您也可以使用包对象,例如,在此处查看scala.math包对象https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src//library/scala/math/package.scala

是的,我认为这是正常的。

对于我的大多数类,我都创建了一个配套对象来处理那里的一些初始化/验证逻辑。例如,如果构造函数中的参数验证失败,则可以在伴随对象应用方法中返回OptionEither,而不是抛出异常:

class X(val x: Int) {
require(x >= 0)
}
// ==>
object X {
def apply(x: Int): Option[X] =
if (x < 0) None else Some(new X(x))
}
class X private (val x: Int)

在伴随对象中,可以添加许多额外的逻辑,例如不可变对象的缓存。

如果不需要同时发送消息,对象也可以在实例之间发送信号:

object X {
def doSomething(s: String) = ???
}
case class C(s: String)
class A extends Actor {
var calculateLater: String = ""
def receive = {
case X => X.doSomething(s)
case C(s) => calculateLater = s
}
}

对象的另一个用例是缩小元素的范围:

// traits with lots of members
trait A
trait B
trait C
trait Trait {
def validate(s: String) = {
import validator._
// use logic of validator
}
private object validator extends A with B with C {
// all members of A, B and C are visible here
}
}
class Class extends Trait {
// no unnecessary members and name conflicts here
}

最新更新