2017-04-13 171 views
1

我們使用大小寫類來表示在客戶端和服務器之間傳輸的JSON對象。除了我們已經生活了很長一段時間以外,它一直在努力工作,我想知道是否有人有一個聰明的方法。這可能會減少代碼重複

比方說,我有一個用戶對象具有ID,名字,姓氏和電子郵件地址。一旦用戶被保存到數據庫中,他有一個分配給他的id(Int),因此對於處理現有用戶的客戶端和服務器之間的所有通信,該id是必填字段。事實上,只有一種情況是不需要id字段,那是用戶第一次被保存的情況。我們目前正在處理這一問題的方法是用一個案例類,看起來像這樣:

case class User(id: Option[Int], firstName: String, lastName: String, email:String) 

在所有情況下,除了最初的保存,該ID是Some併爲初始保存ID始終None所以我們發現自己使用id.getOrElse(0)經常。 (有時候,我們會做一個.get但感覺很髒。)

我很想有是有id: Int領域的現有用戶和沒有id字段的對象在所有的新用戶對象,但不宣所有其他領域在兩個單獨的案例分類中兩次。但是,我看不到方便的方法。我也不喜歡爲新用戶的id字段使用'魔術'數字。

有沒有人有更好的解決這個問題?

+0

的方法之一是使用繼承,即類用戶沒有ID,但InitializedUser從用戶擴展添加這一個領域 –

+0

同樣的問題存在於數據庫中。請查找Rob Norris的演講「固定點類型的純數據庫編程」。 – ashawley

+0

@Lashane我不認爲這有幫助。子類必須重新聲明構造函數的其他字段才能填充超類。除非我錯過了一些東西。 –

回答

1
case class User[+IdOpt <: Option[Int]](idOpt: IdOpt, firstName: String, lastName: String, email:String) 
object User { 
    // Type aliases for convenience and code readability 
    type New = User[None.type] 
    type Saved = User[Some[Int]] 
    type Value = User[Option[Int]] // New or Saved 

    implicit class SavedOps(val user: Saved) extends AnyVal { 
    def id: Int = user.idOpt.get 
    } 
} 

測試:

scala> val billNew = User(None, "Bill", "Gate", "[email protected]") 
billNew: User[None.type] = User(None,Bill,Gate,[email protected]) 

scala> billNew.id 
<console>:17: error: value id is not a member of User[None.type] 
     billNew.id 
      ^

scala> val billSaved = billNew.copy(idOpt = Some(1)) 
billSaved: User[Some[Int]] = User(Some(1),Bill,Gate,[email protected]) 

scala> billSaved.id 
res1: Int = 1 
+0

這很有趣,給了我一些想法。這並不完全符合我的想法,但是因爲這是我得到想法的地方,所以我會接受它作爲答案,並將我作爲一個單獨答案發布。 –

0

如果你將它從用戶身上隱藏起來,擁有一個幻數並不是一個可怕的想法。實際上它是一種常見的模式,Slick使用它。您可以忽略要插入的對象的id值。

所以,你可以通過使構造包私人

case class User private[db](id: Int, firstName: String, lastName: String, email:String) 

開始,然後提供一個同伴對象爲用戶沒有ID創建

object User{ 
    def apply(firstName: String, lastName: String, email: String): User = User(-1, firstName, lastName, email) 
} 

現在你可以構建它,就好像ID不是必需的

val user = User("first","last","email") 
+1

我不喜歡魔術數字的事情是,我必須「知道」我正在處理的對象是否具有有效的ID,或者我必須檢查它是否等於幻數。那時我已經失去了我的類型安全性,因爲類型不告訴我它是新用戶還是現有用戶。誠然,我有一個可選的id相同的問題,但與可選的id編譯器不會讓我忘記了id的有效性。 –

1

這是我們結束了,現在去。

trait Resource[T <: Option[Int]] { 
    def idOpt: T 
} 

object Resource { 
    type IsSome = Some[Int] 
    implicit class SomeOps[R <: Resource[IsSome]](val resource: R) { 
    def id: Int = resource.idOpt.get 
    } 
} 

這使我們能夠使用它像這樣:

case class User[T <: Option[Int]](idOpt:T, firstName:String, lastName:String, email:String) extends Resource[T] 
case class Company[T <: Option[Int]](idOpt:T, companyName: String) extends Resource[T] 

val u1 = User(None, "Bubba", "Blue", "[email protected]") 
val u2 = User(Some(1), "Forrest", "Gump", "[email protected]") 
u1.id // <-- won't compile 
u2.id // <-- compiles