在Scala中使用命令模式时改进代码组织

时间:2015-02-10 01:31:18

标签: scala code-organization command-pattern

我会在我的域名中直接解释这一点,因为否则会让人感到困惑......

在MtG中,有可替换的事件,例如获得生命。我想在事件执行之前使用部分函数来进行替换。为此,每个事件都需要是案例类的实例。

//a replaceable event
trait Event { def execute(): Unit }
//an effect that replaces an event
type ReplacementEffect = PartialFunction[Event, Event]

//applies all effects to the event. If a replacement doesn't match, the
//event remains unchanged
def replace(event: Event, effects: ReplacementEffect*) =
  effects.foldLeft(event) { case (event, effect) => effect.applyOrElse(event, identity[Event] _) }

def executeReplaced(event: Event) = replace(event, activeEffects: _*).execute()

//example: A player's life points

var _life = 20

//one instance of a replaceable event
case class GainLife(life: Int) extends Event {
  def execute() = {
    // ====== here goes the Interesting Game Logic
    _life += life
  }
}

def gainLife(life: Int) =
  // ====== the place where Interesting Game Logic belongs logically
  executeReplaced(GainLife(life))

//execution:

val double: ReplacementEffect = {
  //gain twice as much life
  case GainLife(x) => GainLife(x * 2)
}
//one effect is active
val activeEffects = List(double)

//gain six life
println(_life)
gainLife(3)
println(_life)

如果我没有替换要求,我可以将(部分)压缩到以下内容:

//a replaceable event
type Event = () => Unit

//example: A player's life points

//one instance of a replaceable event
def gainLife(life: Int) = executeReplaced { () =>
  _life += life
}

我更喜欢这段代码的是,有趣的游戏逻辑嵌套在它所属的gainLife方法中。 是否有可能在本地保留更多内容,即使我需要案例类?我能想到的最好的是这个,但除了看起来笨拙之外,GainLife案例类是私有的块,所以它可以像没有案例类一样可用:

executeReplaced {
  case class GainLife(life: Int) extends Event {
    def execute() = {
      _life += life
    }
  }
  GainLife(life)
}

离开我的域名,问题基本上是让命令模式看起来很漂亮:命令的逻辑属于一个地方,我想在那里声明它。但是我必须在同一个地方定义命令的参数,并且我很难将它们提供给位于其他地方的代码。如果我将数据与逻辑分开,除了逻辑所在的位置之外,我无法在任何地方执行命令,并且它会再次破坏代码局部性。

1 个答案:

答案 0 :(得分:0)

你可以使Event成为一个密封的特征,这意味着你只能在同一个文件中创建子类型,这样Scala编译器就会知道所有可能的子类型,并且你可以很好地在其他地方进行模式匹配(你仍然会得到紧密耦合)如果没有为某种类型的事件定义行为,则会出现编译错误。

sealed trait Event
case class GainLife(n: Int) extends Event
case class GainScore(n: Int) extends Event

然后在其他地方

var life, score = 5
event match {
  case GainLife(n) => life += n
  case GainScore(n) => score += n
}