Mam scenariusz, w którym chcę zaimplementować wariant schematu Cake, ale dodając domyślną funkcję klasy (Spark DataFrame).Implementowanie szablonu Cake z domyślną funkcjonalnością
Więc w zasadzie, chciałbym być w stanie uruchomić kod jak poniżej:
trait Transformer {
this: ColumnAdder =>
def transform(input: DataFrame): DataFrame = {
input.addColumn("newCol")
}
}
val input = sqlContext.range(0, 5)
val transformer = new Transformer with StringColumnAdder
val output = transformer.transform(input)
output.show
i znaleźć rezultat jak następuje:
+---+------+
| id|newCol|
+---+------+
| 0|newCol|
| 1|newCol|
| 2|newCol|
| 3|newCol|
| 4|newCol|
+---+------+
Moim pierwszym pomysłem było zdefiniuj niejawne klasy tylko w podstawowych cechach:
trait ColumnAdder {
protected def _addColumn(df: DataFrame, colName: String): DataFrame
implicit class ColumnAdderRichDataFrame(df: DataFrame) {
def addColumn(colName: String): DataFrame = _addColumn(df, colName)
}
}
trait StringColumnAdder extends ColumnAdder {
protected def _addColumn(df: DataFrame, colName: String): DataFrame = {
df.withColumn(colName, lit(colName))
}
}
i działa, ale nie byłem całkowicie zadowolony z tego podejścia, z powodu powielania sygnatur funkcji. Więc pomyślałem o innym podejściu, wykorzystując implicit def
strategię (przestarzałe):
trait ColumnAdder {
protected implicit def columnAdderImplicits(df: DataFrame): ColumnAdderDataFrame
abstract class ColumnAdderDataFrame(df: DataFrame) {
def addColumn(colName: String): DataFrame
}
}
trait StringColumnAdder extends ColumnAdder {
protected implicit def columnAdderImplicits(df: DataFrame): ColumnAdderDataFrame = new StringColumnAdderDataFrame(df)
class StringColumnAdderDataFrame(df: DataFrame) extends ColumnAdderDataFrame(df) {
def addColumn(colName: String): DataFrame = {
df.withColumn(colName, lit(colName))
}
}
}
(pełny kod powtarzalne, w tym dodatkową cechę-modułu można znaleźć here)
tak, chciałem zapytać które podejście jest najlepsze i czy może istnieć inny lepszy sposób osiągnięcia tego, czego chcę.