【问题标题】:Type inference for generic strategy pattern in ScalaScala 中通用策略模式的类型推断
【发布时间】:2014-07-29 20:44:26
【问题描述】:

我想完成什么

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

代码是什么样子的

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

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

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

问题

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

【问题讨论】:

  • 我刚拿了你的代码,复制粘贴到工作表中。通过一些非常小的调整,它似乎可以工作,我看不出有什么理由不这样做。你能提供编译器的实际错误吗? (我测试的代码是gist.github.com/agenovese/82dc31743641f059adf2,评论太长,不是真正的答案,所以我把它链接了)
  • @AngeloGenovese,谢谢。请尝试使用 StrategyOne 和 StrategyTwo 中函数内的所有值。我会更新问题。
  • 另外,我在 REPL 中重新检查了我的示例,在这种情况下,它确实可以编译。但是请注意,映射策略的类型例如变为_ >: Double with Int <: AnyVal。从长远来看,这会破坏我的代码。

标签: scala oop generics type-inference


【解决方案1】:

在这里,抽象类型成员应该比类型参数更能帮助您。确实,您最想绕过Strategys 而不关心它们的两种类型(包括将它们放在地图中)。在某一时刻(在 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,正如我在 cmets 中所写的(路径相关类型,如果您想在 Google 上搜索这个概念)。

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

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

如您所愿。它会很好地进行类型检查。

【讨论】:

  • 在您的 Worker 示例中,应该是:val res = strategy.doSomething(5)
  • 我不知道这是可能的。 Scala 语言总是让我感到惊讶。
猜你喜欢
  • 1970-01-01
  • 2012-09-02
  • 2017-06-29
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2012-11-29
  • 1970-01-01
  • 2020-10-12
相关资源
最近更新 更多