Scala 中带有泛型的类型安全可组合构建器链
Typesafe composable builder chain in Scala with generics
我正在尝试构建一种模式,用户可以通过该模式实现一个简单的接口,该接口采用一种类型的对象和 returns 另一种类型的对象,然后还有某种类型的链对象,它由一系列这些对象组成变换。
我遇到的问题是在 Scala 中获取正确的泛型类型 - 我的 Scala-foo 还没有那么高,所以非常感谢任何建议,包括告诉我我正在这样做 wrong/non-scala方式!
trait Builder[INPUT, OUTPUT] {
var input: Class[INPUT]
var output: Class[OUTPUT]
def process(input: RDD[INPUT]): RDD[OUTPUT]
}
class ComposableBuilder[INPUT, OUTPUT](input: Class[INPUT], output: Class[OUTPUT], phases: Seq[Phase[Any, Any]]) {
def appendBuilder[U](phase: Phase[OUTPUT, U]): ComposableBuilder[INPUT, U] = {
new ComposableBuilder[INPUT, U](input.class, phase.output.class, phases :+ phase)
}
}
因此,示例用法为:
ComposableBuilder(Seq(
ModelEnricher(),
CollateRecordsByKey(),
RecordBuilder(),
)).execute(input)
很明显,对于任何连续对 builder[0].output == builder[1].input
中的构建器序列,存在一个隐含的约束
我不确定您为什么要使用存储 Class
信息的变量。仅使用标准泛型,解决方案应该简单得多:
trait Builder[A,B] {
def process(input: A): B
}
case class ComposedBuilder[A,B,C](b1: Builder[A,B], b2: Builder[B,C]) extends Builder[A,C] {
def process(input: A): C = b2.process(b1.process(input))
}
然后你可以让你的建造者:
object Int2DoubleBuilder extends Builder[Int, Double] { def process(input: Int): Double = input.toDouble }
object Double2StringBuilder extends Builder[Double,String] { def process(input: Double): String = f"$input%.2f" }
object StringPadBuilder extends Builder[String,String] { def process(input: String): String = "000" + input }
并使用它们:
val builder = ComposedBuilder(ComposedBuilder(Int2DoubleBuilder, Double2StringBuilder), StringPadBuilder)
builder.process(423)
// 000423.00
Samir 的评论很有道理。如果你的情况真的这么简单,那么你可以使用内置的 Function1
特性来免费获得一些不错的功能。所以你可以让每个构建器实现一个 A => B
函数:
object Int2DoubleBuilder extends (Int => Double) { def apply(input: Int): Double = input.toDouble }
object Double2StringBuilder extends (Double => String) { def apply(input: Double): String = f"$input%.2f" }
object StringPadBuilder extends (String => String) { def apply(input: String): String = "000" + input }
并使用它们:
val builder = Int2DoubleBuilder andThen Double2StringBuilder andThen StringPadBuilder
builder(423)
// 000423.00
我正在尝试构建一种模式,用户可以通过该模式实现一个简单的接口,该接口采用一种类型的对象和 returns 另一种类型的对象,然后还有某种类型的链对象,它由一系列这些对象组成变换。
我遇到的问题是在 Scala 中获取正确的泛型类型 - 我的 Scala-foo 还没有那么高,所以非常感谢任何建议,包括告诉我我正在这样做 wrong/non-scala方式!
trait Builder[INPUT, OUTPUT] {
var input: Class[INPUT]
var output: Class[OUTPUT]
def process(input: RDD[INPUT]): RDD[OUTPUT]
}
class ComposableBuilder[INPUT, OUTPUT](input: Class[INPUT], output: Class[OUTPUT], phases: Seq[Phase[Any, Any]]) {
def appendBuilder[U](phase: Phase[OUTPUT, U]): ComposableBuilder[INPUT, U] = {
new ComposableBuilder[INPUT, U](input.class, phase.output.class, phases :+ phase)
}
}
因此,示例用法为:
ComposableBuilder(Seq(
ModelEnricher(),
CollateRecordsByKey(),
RecordBuilder(),
)).execute(input)
很明显,对于任何连续对 builder[0].output == builder[1].input
我不确定您为什么要使用存储 Class
信息的变量。仅使用标准泛型,解决方案应该简单得多:
trait Builder[A,B] {
def process(input: A): B
}
case class ComposedBuilder[A,B,C](b1: Builder[A,B], b2: Builder[B,C]) extends Builder[A,C] {
def process(input: A): C = b2.process(b1.process(input))
}
然后你可以让你的建造者:
object Int2DoubleBuilder extends Builder[Int, Double] { def process(input: Int): Double = input.toDouble }
object Double2StringBuilder extends Builder[Double,String] { def process(input: Double): String = f"$input%.2f" }
object StringPadBuilder extends Builder[String,String] { def process(input: String): String = "000" + input }
并使用它们:
val builder = ComposedBuilder(ComposedBuilder(Int2DoubleBuilder, Double2StringBuilder), StringPadBuilder)
builder.process(423)
// 000423.00
Samir 的评论很有道理。如果你的情况真的这么简单,那么你可以使用内置的 Function1
特性来免费获得一些不错的功能。所以你可以让每个构建器实现一个 A => B
函数:
object Int2DoubleBuilder extends (Int => Double) { def apply(input: Int): Double = input.toDouble }
object Double2StringBuilder extends (Double => String) { def apply(input: Double): String = f"$input%.2f" }
object StringPadBuilder extends (String => String) { def apply(input: String): String = "000" + input }
并使用它们:
val builder = Int2DoubleBuilder andThen Double2StringBuilder andThen StringPadBuilder
builder(423)
// 000423.00