我有一个Scala应用程序,其trait
实现了一些功能,而class
扩展了trait
。
上面提到的类还有一个函数,它使用它的参数调用父特征中定义的函数。
我在使用Scala的Spark + Kafka实现中观察到了这一点。我猜这是某种设计模式,但我不知道哪一个。是蛋糕模式吗?依赖注入模式?或其他什么?
以下是我所指的代码:
trait SparkApplication {
def sparkConfig: Map[String, String]
def withSparkContext(f: SparkContext => Unit): Unit = {
val conf = new SparkConf()
sparkConfig.foreach { case (k, v) => conf.setIfMissing(k, v) }
val sc = new SparkContext(conf)
f(sc)
}
}
trait SparkStreamingApplication extends SparkApplication {
def withSparkStreamingContext(f: (SparkContext, StreamingContext) => Unit): Unit = {
withSparkContext { sc =>
val ssc = new StreamingContext(sc, Seconds(streamingBatchDuration.toSeconds))
ssc.checkpoint(streamingCheckpointDir)
f(sc, ssc)
ssc.start()
ssc.awaitTermination()
}
}
}
答案 0 :(得分:2)
这里使用的是什么(虽然可能有错误)是所谓的贷款模式,以这种方式调用,因为它在您想要管理生命周期时非常有用资源(在您的情况下为SparkContext
),同时允许用户定义资源的使用方式。
这方面的一个典型示例是文件:您想要打开文件,阅读它的内容,然后在完成后立即将其关闭,而不会让用户犯错误而忘记关闭资源。您可以按如下方式实现:
import scala.io.Source
// Read a file at `path` and allow to pass a function that iterates over lines
def consume[A](path: String)(f: Iterator[String] => A): A = {
val source = Source.fromFile(path)
try {
f(source.getLines)
} finally {
source.close()
}
}
然后你按照以下方式使用它(在示例中,只打印与其数字配对的所有行):
consume("/path/to/some/file")(_.zipWithIndex.foreach(println))
正如您可能已经注意到,您的代码中有一些非常接近的事情,唯一的区别是您管理的生命周期的资源是SparkContext
。
关于我最初提到的可能错误,它认为你借用一个你永远不会关闭的SparkContext
这个事实。这可能没问题,但 Loan Pattern 的主要方面正是在管理资源时最小化错误表面的方面。你可能有兴趣做以下事情(你想检查方法的最后一行):
def withSparkContext(f: SparkContext => Unit): Unit = {
val conf = new SparkConf()
sparkConfig.foreach { case (k, v) => conf.setIfMissing(k, v) }
val sc = new SparkContext(conf)
f(sc)
sc.stop() // shutdown the context after the user is done
}
您可以阅读有关此模式的更多信息here。
作为旁注,您可能会对this project感兴趣,它会在托管资源周围创建一个非常好的和惯用的界面。