背景
工作需要,需要编写一个配置文件来配置一些函数。这些函数会应用于特定数据只上。我们都很熟悉java怎么实现这个东西,可以用反射、或者写一堆if else判断。scala崇尚函数式编程,笔者不才。尝试用3种思路实现以供参考:
object FunctionLoaderTest {
//函数获取器,反射实现。
import reflect.runtime.universe._
implicit class FunctionReflector(val functionConf: String) extends AnyVal {
def getFunc: (Double, Double) => Double = {
val functionConfSplit = functionConf.split("[\\(\\)]")
val weight = try {
if (functionConfSplit.length > 1) functionConfSplit(1).toDouble else 1.0
} catch {
case _: Throwable => 1.0
}
try {
lazy val mirror = runtimeMirror(getClass.getClassLoader)
lazy val instanceMirror = mirror.reflect(FunctionLoaderTest)
lazy val methodSymbol = instanceMirror.symbol.toType.decl(TermName(functionConfSplit.head)).asMethod
lazy val methodMirror = instanceMirror.reflectMethod(methodSymbol)
def func(a: Double, b: Double): Double = methodMirror(weight, a, b).asInstanceOf[Double]
func
} catch {
case _: Throwable => (a: Double, b: Double) => 0.0
}
}
}
//函数获取器,注册实现。
implicit class FunctionGetterDirect(val functionConf: String) extends AnyVal {
def getFunc: (Double, Double) => Double = {
val functionConfSplit = functionConf.split("[\\(\\)]")
val weight = try {
if (functionConfSplit.length > 1) functionConfSplit(1).toDouble else 1.0
} catch {
case _: Throwable => 1.0
}
functionConfSplit.head match {
case "add" => add(weight)
case "multiply" => multiply(weight)
case _ => (a: Double, b: Double) => 0.0
}
}
}
//函数获取器,复合函数实现。
implicit class FunctionGetterComposite(val functionConf: String) extends AnyVal {
def getFunc: (Double, Double) => Double = {
val functionConfSplit = functionConf.split("[\\(\\)]")
val weight = try {
if (functionConfSplit.length > 1) functionConfSplit(1).toDouble else 1.0
} catch {
case _: Throwable => 1.0
}
(a: Double, b: Double) => {
functionConfSplit.head match {
case "add" => add(weight)(a, b)
case "multiply" => multiply(weight)(a, b)
case _ => 0.0
}
}
}
}
def add(weight: Double)(a: Double, b: Double) = weight * (a + b)
def multiply(weight: Double)(a: Double, b: Double) = weight * a * b
}
object Test {
def main(args: Array[String]): Unit = {
import FunctionLoaderTest.FunctionReflector
println("add(2)".getFunc(2, 3))
println("multiply(2)".getFunc(2, 3))
}
}
反射的好处就是你新增函数不需要注册,也就是不需要match来实现类似switch的操作,这里反射加lazy主要是由于本人这个是用于spark的,spark在广播的时候会把这个复合函数广播出去,其中有部分reflection的类不可序列化,写成lazy他广播的是空,到executor用的时候才去真正执行反射。注册方式实现的结果是真正返回了那个函数,复合函数实现的是一个炒鸡复杂的函数根据情况判断调用哪个。