简体   繁体   中英

Scala: difference between usage of singleton vs trait/class

I wanted to know the implications of using mixins vs singletons. As most of us agree, singletons are evil because they maintain state and it's hard to keep track of that state. Traits are superior because they can be mixed-in, mocked easily etc.. But I wanted to know the actual implications of what's happening under the hood: How using mixins does not cause problems ?

The below code samples are just for the sake of demonstration of my problem.

Approach #1: Using singletons all over:

object Server {
  def fetchData(input: Input): String = ...
}
object Servers { 
  val inMemoryServers: Seq[Server] = ...
  def randInMemServer = Random.shuffle(Servers.inMemoryServers).head
}
object AppClient {
  def execute(input: Input) = {
    Servers.randInMemServer.fetchData(input)
  }
}

Approach #2: Using mixins

trait Server2 {
  def fetchData(input: Input): String = ...
}
trait Servers2 extends Server2 { 
  val inMemoryServers: Seq[Server] = ...
  val httpServers: Seq[Server] = ...
}
case class DataA(input: Input) extends Servers2 {
  def getA = inMemoryServers
}
case class DataB(input: Input) extends Servers2 {
  def getB = ...
}
object DataC extends Servers2 {
  def getC(input: Input) = ...
}
object AppClient2 {
  def execute(input: Input) = {
    val a = DataA(input).getA // line 1
    val b = DataB(input).getB // line 2
    val c = DataC.getC(input) // line 3
  }
}

How many instances of Server2 and Servers2 are created in AppClient after line 3 ?

Say if Server2 had a variable to create a threadpool (with say 5 threads, see below), how many there are created after (line 3) ?

trait Server2 {
  val pool: Threadpool = ... // create a pool of 5 threads
  def fetchData(input: Input): String = ...
}

What are the implications if the Server2 declared the members as def s instead of val s as shown below ? Are new thread pools created in each of the lines 1, 2 and 3 ?

trait Servers3 extends Server2 { 
  def threadPool: Threadpool = ...
  def inMemoryServers: Seq[Server] = ...
}   

In general you should use object singletons for stuff like application-global constants (like enums) or state variables.

Traits are an alternative approach to multiple inheritance which let you share functionality across types without defining a hierarchy (to answer your question, you dont really create instances of traits). IIRC, In scala, a def is a method declaration and a val is an immutable constant.

AppClient2#execute discards Server2's instances, but creates those each time calling "DataA(input)" or "DataB(input)". "val" will be evaluated when instance is created, and "def" will be evaluated when the function is called. So if pool is daclared as "val", 5 threads are created when every calling DataX(input).(But discarded)

Because DataC is singleton, "DataC.getC(input)" will not create thread pools twice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM