在Scala中键入通用策略模式的推断

时间:2014-07-29 20:44:27

标签: scala oop generics type-inference

我想要完成的事情

我想利用策略模式,而策略类有类型参数。

代码是什么

假设我有以下通用抽象策略类:

abstract class Strategy[T, V]() {
    def doSomething(x: Int): V
    def unDoSomething(x: V): T
}

我现在推出两个具体的策略:

class StrategyOne() extends Strategy[Int, String] {
    def doSomething(x: Int): String = { x.toString() }
    def unDoSomething(x: String): Int = { x.toInt }
}


class StrategyTwo() extends Strategy[Double, List[Int]] {
    def doSomething(x: Int): List[Int] = { List(x, 10, 20)}
    def unDoSomething(x: List[Int]): Double= { x.reduceLeft(_ + _) + 0.1 }
}

现在,我们有一个使用该策略的类:

class Worker[T, V](strategy: Strategy[T, V]) { 
    def run() {
        val res = strategy.doSomething(5) //res is a T
        val res2 = strategy.unDoSomething(res) //res2 is a V
        println(res2)
    }

}

正如预期的那样,我现在能够实例化具有明确类型的新工作者:

val worker1 = new Worker(new StrategyOne())
val worker2 = new Worker(new StrategyTwo())

问题

但是,我也想利用某种动态策略选择,如下所示:

val strategies = Map("one" -> new StrategyOne(), "two" -> new StrategyTwo())
val worker = new Worker(strategies(args(0)))

当然,编译器告诉我,我想要的是不可能的,因为不能推断出任何类型。

问题

我知道这个星座是不幸的,但我需要TVWorker的类型。 是否有可能使这种模式适用于这种特定情况?

1 个答案:

答案 0 :(得分:5)

抽象类型成员应该在这里帮助你而不仅仅是输入参数。实际上,你大多想要传递Strategy s,而不必过多关注他们的两种类型(包括将它们放在地图中)。并且在某一点(Worker),您需要类型。

所以我建议如下(您应该在此模型中为VT提供更多描述性名称,但我无法弄清楚它们的含义,因此我将它们保留原样):

abstract class Strategy {
  type T
  type V

  def doSomething(x: Int): V
  def unDoSomething(x: V): T
}

class StrategyOne extends Strategy {
  type T = Int
  type V = String
  def doSomething(x: Int): String = {...}
  def unDoSomething(x: String): Int = {...}
}

class StrategyTwo extends Strategy {
  type T = Double
  type V = List[Int]
  def doSomething(x: Int): List[Int] = {...}
  def unDoSomething(x: List[Int]): Double= {...}
}

class Worker(strategy: Strategy) { 
  def run(): Unit = {
    val res = strategy.doSomething(5) //res is a strategy.T
    val res2 = strategy.unDoSomething(res) //res2 is a strategy.V
    println(res2)
  }
}

在这种情况下,推断出resres2的类型。但是,如果你需要写下他们的类型,它们是strategy.Tstrategy.V,就像我在评论中写的那样(路径依赖类型,如果你想谷歌这个概念)。

您仍然可以轻松制定策略:

val worker1 = new Worker(new StrategyOne)
val worker2 = new Worker(new StrategyTwo)

现在你也可以这样做:

val strategies = Map("one" -> new StrategyOne, "two" -> new StrategyTwo)
val worker = new Worker(strategies(args(0)))

按照您的要求。而这一切都很好听。