2012-01-10 57 views
17

我想根據具體類可能具有的各種屬性,從特徵中組裝域對象。當我的對象是可變的時,這是非常簡單的。例如:不變的類層次結構中的多態更新

trait HasHitPoints { var hitPoints: Int = 100 } 
trait HasBearing { var bearing: Double = 0 } 

class Ship extends HasHitPoints with HasBearing 
class Base extends HasHitPoints 

val entities = new Ship :: new Base :: Nil 
entities.collect { case h: HasHitPoints => h.hitPoints += 10 } 

特別地,我可以多晶型讀或不知道具體類型更新任何HasHitPoints實例。

什麼是最好的方式來實現這與不​​可變對象?如果我很高興剛纔讀的屬性,然後我可以這樣做:

trait HasHitPoints { val hitPoints: Int } 
trait HasBearing { val bearing: Double } 

case class Ship(hitPoints: Int, bearing: Double) extends HasHitPoints with HasBearing 
case class Base(hitPoints: Int) extends HasHitPoints 

val things = Ship(50, 0) :: Base(100) :: Nil 

val totalHitPoints = things.collect { case h: HasHitPoints => h.hitPoints }.sum 

而且,我可以很容易地修改使用copy具體的類,如果我知道確切類型。例如,困難的部分是更新任意的HasHitPoints。如果我有很多具體的類和許多不同的屬性,我可能喜歡混入,那麼避免模板代碼爆炸的最佳方案是什麼?

回答

1

您可能有添加一個例如用你的traits抽象def withHitPoints(points:Int)方法,它返回具有不同屬性值的容器對象的副本。這減少了使用的東西,如:

val damagedActors = actors map { actor => actor.withHitPoints(actor.hitPoints - 10) } 

但是,否則將要求每具體類屬性的額外的方法,所以我不知道它真正解決您的問題。這對於像Scala這樣的靜態語言來說並不合適(我也不會爲這個特定的用例打擾不可變性);這裏一個不可變的解決方案可能是動態語言的更好選擇。

+0

沒錯,儘管N個更新方法在M個具體類中的混亂是我希望避免的。正如你指出的那樣,在動態語言中它會非常簡單。這個用例顯然非常簡單,但我認爲當你將更新與不可變的繼承層次結構混合在一起時,這個問題經常會出現。 – 2012-01-10 13:17:29

+0

我不會爭辯說這是Scala處理不好的問題,但根據我的經驗,這是一種不會經常出現的情況 - 除非您在建模的這段時間已經死了。 – 2012-01-11 13:32:25

+2

可能,雖然我不知道薩皮爾 - 沃爾夫事件的程度如何:語言無法很好地處理的場景會變成不經常出現的場景...... – 2012-01-11 14:28:58

1

您希望避免M個具體類中的N個更新方法。不過,我覺得這是不可能的。您將需要訪問複製方法或至少每個具體類的構造函數。它們都不能被抽象化,如下所述:Case class copy() method abstraction 所以最後你總是會得到N×M'樣板'的代碼。