2014-02-26 26 views
3

我使用ORM模型的案例類。每個模型都有一個ID,但該ID不應公開訪問。所以我有一個父特徵Scala案例類副本成員的父母特質時調用複製()

trait WithId { 
    private var id: Long = 0 
} 

和大量的case類(模型)的繼承了它現在

case class C1(a: Int, b: String) extends WithId 
case class C2(...) extends WithId 
... 

,如果有人叫副本()上的情況下類,它不用它複製id,但將其設置爲0.

val c1 = C1(3, "bla") 
//Set c1.id to a value != 0 
val c2 = c1.copy(b="bla2") 
//c1.id !=0, but c2.id = 0 

我希望它也複製id。

由於我有很多這些案例類,我寧願在案例類自己有儘可能少的代碼。因此,在每種情況下實現copy()方法將會有很多樣板代碼。

有沒有辦法在trait中實現某些東西,使copy()也複製id?也許使用宏的東西?還是有另一種方式我根本沒有想過?

編輯

我可以覆蓋id字段在每種情況下類像

case class C1(a: Int, b: String, protected var id: Long) 

然後,它會被複制。但這也是我必須爲每個case類編寫的樣板代碼,並且很難解釋爲什麼必須將id字段添加到case類,儘管您從不會注意到它,或者在使用case類時可以在任何其他地方使用它。如果可能,我想避免這種情況。

+0

我無法幫您解答,但我建議您付出一些努力並重構您的代碼。在案例類中添加(可變)字段是一種臭味的代碼異味。例如。兩個不同'id'的實例可能是相同的。 – poroszd

+0

@poroszd - 儘管可變性可能是一個問題,但也不難將兩個實例分配錯誤的ID。可變ID的問題在於,如果不是,您可以指望它穩定。 –

+0

該ID不能從ORM框架外部訪問。我必須在內部使它變化,因爲ORM模型在初始化時id = 0,並且一旦對象被添加到數據庫就更新此id。但是你是對的,我應該重寫==運算符。 – Heinzi

回答

1

如果我是你,我會添加一個ID攜帶令牌:

class IdToken private[myPackage] (val id: Int) { 
    override def equals(a: Any) = a match { 
    case tok: IdToken => id == tok.id 
    case _ => false 
    } 
    override def hashCode = id.hashCode^0x157135 
    override def toString = "" 
} 
object IdToken { 
    private var lastId = 0 
    private[myPackage] def next = { lastId += 1; new IdToken(lastId) } 
} 

由於構造函數是私有你的包,沒有其他人,但你可以創建這些令牌。

然後你寫你的特質是

trait WithID { protected def idToken: IdToken } 

case class C1(a: Int, b: String, protected val idToken: IdToken = IdToken.next) extends WithID { 
    def checkId = idToken.id 
} 

(在這裏你只需要checkId下面的測試),所以你可以

scala> C1(5, "fish") 
res1: C1 = C1(5,fish,) 

scala> res1.copy(a = 3) 
res2: C1 = C1(3,fish,) 

scala> res1.checkId == res2.checkId 
res3: Boolean = true 

,但你可以」 t從外部代碼訪問令牌,因爲valprotected

希望這是足夠好的封裝。

+0

謝謝。這是迄今爲止我看到的最好的解決方案。但我希望避免每個案例類的代碼,因爲我將會有很多這樣的代碼。看到我上面的編輯。 – Heinzi

+0

@Heinzi - 你唯一的選擇是一個全局對象註冊表,一個新創建的對象檢查並從中抽取其ID。我不推薦這個。那麼,那個和宏。你可以用宏做很多事情,但這並不簡單,直到你找到一個可行的解決方案時,很難判斷一個解決方案是否存在。 –