2017-01-11 11 views
9

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ę.

Odpowiedz

1

zaledwie dwa skróty, ale nic naprawdę zdumiewające:

trait ColumnAdder { 
    protected implicit def columnAdderImplicits(df: DataFrame): ColumnAdderDataFrame 
    abstract class ColumnAdderDataFrame { 
    def addColumn(colName: String): DataFrame 
    } 
} 

trait StringColumnAdder extends ColumnAdder { 
    override def columnAdderImplicits(df: DataFrame) = 
    new ColumnAdderDataFrame { 
     def addColumn(colName: String): DataFrame = 
     df.withColumn(colName, lit(colName)) 
    } 
} 

Jeśli jesteś gotów, aby umożliwić -language:reflectiveCalls (należy zdawać sobie sprawę z implikacji), a następnie można również napisać:

trait ColumnAdder { 
    protected implicit def columnAdderImplicits(df: DataFrame): { 
    def addColumn(colName: String): DataFrame 
    } 
} 

trait StringColumnAdder extends ColumnAdder { 
    override def columnAdderImplicits(df: DataFrame) = new { 
    def addColumn(colName: String): DataFrame = 
     df.withColumn(colName, lit(colName)) 
    } 
}