如何在创建域模型时为 String 和其他数据类型创建更具体的类型

How to create more specific types for String and other data types while creating domain models

在域建模时,我如何确保两种类型 — UserPlace 不会互换其类型为 Stringname 字段。

trait User {
  val firstName: String
  val lastName: String
}


object User {
  final class Live(val firstName: String,val lastName: String) extends User 
  def apply(firstName: String, lastName: String): User = new Live(firstName, lastName)
}


trait Place {
  val name: String  
}


object Place {
  final class Live(val name: String) extends Place
  def apply(name: String): Place = new Live(name)
}

val a = User("Tushar", "Mathur")
val b = Place("Mumbai")


val c = Place(a.firstName) 
// How do I disable this ^

Scala 支持此功能,如下例所示。有一些库可以减少样板代码,但我只展示最简单的选项:

// define more restrictive String-like types. AnyVal construction can be free from 
// overhead with some caveats.
case class UserName(name: String) extends AnyVal
case class PlaceName(name: String) extends AnyVal

// define your classes (I've changed them a bit for brevity):
case class User(name: UserName)
case class Place(name: PlaceName)

// implicits for convenience of construction:
implicit val strToUserName = UserName(_)
implicit val strToPlaceName = PlaceName(_)

// test it out:
scala> val u = User("user")
u: User = User(UserName(user))

scala> val p = Place("place")
p: Place = Place(PlaceName(place))

// as expected you CAN'T do this:
scala> User(p.name)
<console>:17: error: type mismatch;
 found   : PlaceName
 required: UserName
       User(p.name)
              ^

// comparison test:
scala> p.name == u.name
<console>:16: warning: comparing case class values of types PlaceName and UserName using `==' will always yield false
       p.name == u.name
              ^
res3: Boolean = false

// you can still get at the string value:
scala> p.name
res6: PlaceName = PlaceName(place)

scala> p.name.name
res5: String = place