2015-02-10 123 views
2

使用命令模式的时候,我会直接在我的域名解释这一点,因为它会得到纷繁复杂的提高代码的组织......在斯卡拉

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

离开我的领域,问题基本上使命令模式看起来很好看:命令的逻辑属于一个地方,我想在那里声明它。但是之后我必须在相同的地方定义命令的参数,并且我努力使它们可用于其他地方的代码。如果我将数据从逻辑中分离出来,除了逻辑所在的位置外,我无法执行任何命令,并且会再次破坏代码的局部性。

回答

0

你可以让事件成为一个密封的特质,这意味着你只能在同一个文件中创建子类型,这样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 
} 
+0

这解决了一个问题,但不是我具有一个:我想'生活+ = N'地方用'gainLife'方法,而不是本地与所有其他事件。而且,这组事件并不是我认为“从一开始就知道的”,因此密封并不是这里最好的主意。 – 2015-02-10 09:56:17

+0

对不起,以为你想保持修改逻辑接近状态,不接近事件,我想我误解了你的问题。 – johanandren 2015-02-10 10:22:35