2014-09-30 41 views
4

我正在用不可變對象建模我們的域。用不可變對象模擬繼承+可變狀態

可變的設計

一個基本特徵WObject(世界對象)以及實施像OwnedObj具體操作特性的負載(馬力/老闆/ takeDamage),動產(movementLeft /的moveTo),戰士(攻擊/攻擊)。

在層次結束時,你有一個可變類混合在適當的特質:

class Corvette(var position: Vect2) extends WObject with OwnedObj with Movable with Fighter 

如果用戶想要做的操作(可以說移動的船舶除外),你這樣做:

val opt = objects.collectFirst { case obj: Movable if obj.position == position => obj } 
opt.fold(Log.error(s"Movable at $position not found!")) { obj => 
    obj.moveTo(position) // return type is Unit 
} 

永恆設計

如果的moveTo必須返回一個新的OBJ等等,它返回什麼類型?

我試過使用trait Movable[Self <: Movable[Self]]的方法,但是這需要把Movable [_]帶到任何地方,這些存在類型很快就會失去控制。如果我想要Movable[_] with Fighter[_]?是_同一類型嗎?

我也試圖與類型界限的做法性狀裏面抽象類型自我,但這種開始於以下情況下會很麻煩:

def takeDamage(obj: OwnedObj): obj.Self = if (Random.nextDouble()) obj.takeDamage else obj.self 

鳥巢這一點,你會得到類型,如

def attackReachable(
    data: WObject.WorldObjUpdate[Self] 
): WObject.WorldObjUpdate[data.value._2.Self] 

哪些只是可怕的。

我在考慮繼承和使用composition + typeclasses,但我不太確定如何去做。

例如:

case class WObject(position: Vect2, id: UUID=UUID.randomUUID()) 
case class OwnedObj(owner: Owner) 
case class Movable(movementLeft: Int) 
case class Fighter(attacked: Boolean) 
case class Corvette(obj: WObject, owned: OwnedObj, movable: Movable, fighter: Fighter) 

// Something that has both WObject and Movable 
trait MovableOps[A <: ???] { 
    def moveTo(obj: A, target: Vect2): A 
} 

然後在類型類,這將在伴隨克爾維特對象來實現限定的操作。

但我不知道如何指定約束。

更多關於如何從客戶端實現移動操作?

val opt = objects.collectFirst { case obj: ??? if obj.position == position => obj } 
opt.fold(Log.error(s"Movable at $position not found!")) { obj => 
    objects = objects - obj + obj.moveTo(position) 
} 

幫助讚賞:)

相關:(Movable[T] with Fighter[T] forSome {type T})Polymorphic updates in an immutable class hierarchy

回答

3

可以使用一個存在寫 「相同_用」 的情況。

如果我正確理解了你的attackReachable示例,我不會擔心路徑依賴類型太多。你通常可以讓它們被推斷出來,具體的調用將會有「實際」的類型。隱含的=:=Leibniz參數的戰略使用,你知道類型實際上是相同的,可以阻止事情失控。或者更簡單地說,你可以要求該類型是一樣的:

def doSomething[T <: Moveable { type Self = T }](t: T): T = 
    t.somethingThatReturnsTDotSelf() 

如果你想要去的路線組成,我能想到的是用無形的鏡頭的最好的方法(我不能單片比較鏡頭,因爲我沒有使用它們):

trait Move[A] { 
    val lens: Lens[A, (WObject, Movable)] 
} 
/** This could be implicitly derived with Generic if you really want to - 
or you could use Records. */ 
implicit def moveCorvette = new Move[Corvette] { 
    val lens = lens[Corvette].obj ~ lens[Corvette].movable 
} 

def moveTo[A: Move](obj: A, target: Vect2) = { 
    val l = Lens[A, (Wobject, Movable)] 
    val remainingMoves = l.get(obj)._2.movementLeft - 1 
    l.set(obj)((target, remainingMoves)) 
} 

若要將此到列表,列表中你要麼繼續作爲HList,讓你知道該類型的所有元素(例如,您的列表是Fighter :: Corvette :: HNil型),或者在列表條目中包含存在的證據(例如trait ObjAndMove {type T; val obj: T; val evidence: Move[T]},然後使用List[ObjAndMove]

+0

attackReachable可以做2件事:1)如果目標不能被攻擊,它應該返回'data'。 2)如果可以達到目標,它應該返回更新的數據,其中目標將被改變,Self將被改變並且將其標記爲受到攻擊。由於自我類型1變得不可能,因爲編譯器不知道Self == data.value._2.Self(它是從Self的修改返回的) – arturaz 2014-09-30 11:35:53

+0

也可以詳細說明Monocle vs Shapeless鏡頭嗎? – arturaz 2014-09-30 11:50:37

+0

如果你只有一個不同類型的列表(讓我們說一個WorldObject的基類型),你又怎麼調用moveTo?你是否需要模式匹配到類型類型的類型才能踢入? – arturaz 2014-09-30 12:09:29