scala – 使用不可变对象模拟继承可变状态

我在使用不可变对象建模域时遇到问题.

可变设计

一个基本特征WObject(世界对象)和用于实现特定操作的特征,如OwnedObj(hp / owner / takeDamage),Movable(movementLeft / moveTo),Fighter(攻击/攻击).

在层次结构的末尾,你有一个可变类,它混合了适当的特征:

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必须返回一个新对象,它返回什么类型?

我尝试过使用特性Movable [Self<:Movable [Self]]方法,但这需要将Movable [_]随处可见,而那些存在类型很快就会失控.如果我想要使用战斗机[_]可移动[_]怎么办?是_同一类型? 我也尝试使用类型边界方法在特征内部抽象类型Self,但是在以下场景中开始变得毛茸茸:

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]

哪个太可怕了.

我在考虑放弃继承和使用组合类型类,但我不太清楚如何做到这一点.

例如:

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
}

然后在类型类中定义操作,这将在Corvette伴随对象中实现.

但我不知道如何指定约束.

更多关于如何从客户端实施移动操作?

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)
}

帮助赞赏:)

相关:Polymorphic updates in an immutable class hierarchy

解决方法

你可以使用存在主义来编写你的“相同_ in with”案例:(可动[T] with Fighter [T] forSome {type T}).

如果我已经正确理解了你的attackReachable示例,我就不会太担心路径依赖类型了.您通常可以允许推断它们,具体调用将具有“实际”类型.策略性地使用implicit =:=或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),或者在列表条目中包含证据存在主义(例如特质ObjAndMove {type T; val obj:T; val证据:Move [T]}然后使用List [ObjAndMove])

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...