在Scala中将参数传递给singleton对象
我继承了一个需要扩展的Scala项目,而不是一个单一的庞然大物,它必须被拆分:一些代码需要成为一个库,供所有其他组件使用,还有一些应用程序可能会在以后的某个时候包括进来在Scala中将参数传递给singleton对象,scala,apache-spark,Scala,Apache Spark,我继承了一个需要扩展的Scala项目,而不是一个单一的庞然大物,它必须被拆分:一些代码需要成为一个库,供所有其他组件使用,还有一些应用程序可能会在以后的某个时候包括进来 object Shared { // vals, defs } ===== import Shared._ object Utils1 { // vals, defs } ===== import Shared._ object Utils2 { // vals, defs } ===== impor
object Shared {
// vals, defs
}
=====
import Shared._
object Utils1 {
// vals, defs
}
=====
import Shared._
object Utils2 {
// vals, defs
}
=====
import Shared._
import Utils1._
class Class1(val ...) {
// stuff
}
=====
import Shared._
import Utils2._
class Class2(val ...) {
// more stuff
}
etc.
问题是Utils1
和Utils2
(以及许多其他对象和类)使用Shared
(单例)中的值,现在必须实例化Shared
,因为Shared
中发生的关键事情之一是设置了应用程序名称(通过SparkConf
)以及读取带有数据库连接信息的配置文件
其想法是建立一个多项目的构建,在这里我可以选择需要重新编译的组件并执行它。由于Utils
中的代码几乎被当前存在和即将出现的所有应用程序共享,所以将其保存在一个大型项目中很好,至少我是这么认为的。很难将常见的代码发布到本地存储库中,因为我们还没有本地存储库,并且很难获得拥有本地存储库的许可
需要Utils
单例,因为它们必须是静态的(因为Spark和可序列化性)
当我将Shared
作为一个合适的类时,所有的import
语句都将变得无用,我必须传递一个实例,这意味着我不能使用单例,即使我需要它们。它目前可以工作,因为只有一个应用程序,所以实际上只有一个Shared
实例。将来,每个应用程序仍将只有一个共享的实例,但单个项目中可能定义了多个应用程序/服务
我已经研究过隐式、包对象和依赖项注入,但我不确定这是一条正确的道路。也许我只是看不到什么是显而易见的
长话短说,有没有一种简洁的方法来给出Shared
一个参数或实现我希望实现的目标 作为一个选项,您可以创建SharedClass
类,并使Shared
对象使用不同的构造函数参数在每个应用程序中扩展它:
class SharedClass(val param: String) { // vals ...
}
object Shared extends SharedClass("value")
作为一个选项,您可以创建SharedClass
类,并使用不同的构造函数参数在每个应用程序中扩展Shared
对象:
class SharedClass(val param: String) { // vals ...
}
object Shared extends SharedClass("value")
其中一种方法是使用DynamicVariable
并在初始化Shared
时使其具有值(即,第一次调用任何引用Shared
字段或方法的内容)
考虑以下几点:
/* File: Config.scala */
import scala.util.DynamicVariable
object Config {
val Cfg: DynamicVariable[String] = new DynamicVariable[String](null)
}
/**********************/
/* File: Shared.scala */
import Config._
object Shared {
final val Str: String = Cfg.value
def init(): Unit = { }
}
/*********************/
/* File: Utils.scala */
import Shared._
object Utils {
def Length: Int = Str.length
}
/********************/
/* File: Main.scala */
object Main extends App
{
// Make sure `Shared` is initialized with the desired value
Config.Cfg.withValue("foobar") {
Shared.init()
}
// Now Shared.Str is fixed for the duration of the program
println(Shared.Str) // prints: foobar
println(Utils.Length) // prints: 6
}
当然,这种设置是线程安全的,尽管不是确定性的。以下Main
将随机选择3个字符串中的一个,并将所选字符串及其长度打印3次:
import scala.util.Random
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Main extends App
{
def random(): Int = Random.nextInt(100)
def foo(param: String, delay: Int = random()): Future[Unit] = {
Future {
Thread.sleep(delay)
Config.Cfg.withValue(param) {
Shared.init()
}
println(Shared.Str)
println(Utils.Length)
}
}
foo("foo")
foo("foobar")
foo("foobarbaz")
Thread.sleep(1000)
}
其中一种方法是使用DynamicVariable
并在初始化Shared
时使其具有值(即,第一次调用任何引用Shared
字段或方法的内容)
考虑以下几点:
/* File: Config.scala */
import scala.util.DynamicVariable
object Config {
val Cfg: DynamicVariable[String] = new DynamicVariable[String](null)
}
/**********************/
/* File: Shared.scala */
import Config._
object Shared {
final val Str: String = Cfg.value
def init(): Unit = { }
}
/*********************/
/* File: Utils.scala */
import Shared._
object Utils {
def Length: Int = Str.length
}
/********************/
/* File: Main.scala */
object Main extends App
{
// Make sure `Shared` is initialized with the desired value
Config.Cfg.withValue("foobar") {
Shared.init()
}
// Now Shared.Str is fixed for the duration of the program
println(Shared.Str) // prints: foobar
println(Utils.Length) // prints: 6
}
当然,这种设置是线程安全的,尽管不是确定性的。以下Main
将随机选择3个字符串中的一个,并将所选字符串及其长度打印3次:
import scala.util.Random
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Main extends App
{
def random(): Int = Random.nextInt(100)
def foo(param: String, delay: Int = random()): Future[Unit] = {
Future {
Thread.sleep(delay)
Config.Cfg.withValue(param) {
Shared.init()
}
println(Shared.Str)
println(Utils.Length)
}
}
foo("foo")
foo("foobar")
foo("foobarbaz")
Thread.sleep(1000)
}
Scala中的对象具有可用于此目的的“应用”方法。因此,您的共享对象如下所示
object Shared {
def apply(param1: String, param2: String) = ???
}
现在,Shared的每个客户端都可以传递不同的值。Scala中的对象具有“应用”方法,您可以使用该方法来实现此目的。因此,您的共享对象如下所示
object Shared {
def apply(param1: String, param2: String) = ???
}
现在,Shared的每个客户端都可以传递不同的值。否,您不能向Shared添加参数。在需要时将参数传递给Shared
的方法如何?否,您无法向Shared添加参数在需要时如何将参数传递给Shared
的方法?如何在不同的对象/类中使用Shared
<代码>共享
将仅在每个应用程序的main
中设置,并且值
取决于应用程序。Utils
中的实用程序函数仍然需要获得一个实例,这些实例本身就是单例。我的意思是,你可以在每个应用程序中分别定义这个对象。它仍然是singleton
。我如何在不同的对象/类中使用Shared
<代码>共享将仅在每个应用程序的main
中设置,并且值
取决于应用程序。Utils
中的实用程序函数仍然需要获得一个实例,这些实例本身就是单例。我的意思是,你可以在每个应用程序中分别定义这个对象。它仍然是singleton
。是不是只有我才散发出全局变量的味道?你会建议我把怪物重构成不同的类实例,并从依赖于这些“环境”变量的对象中取出东西吗?@Ian,是的。(从技术上讲,它更像全局常量:值只设置一次)。但是使用Shared
singleton已经有点像使用全局状态了。这个答案只是为了说明它在技术上是如何可能的,我从来没有在生产中使用过这样的东西。(另一种方法是简单地从环境变量或配置文件加载值)一些重构可能是有序的,或者您可以尝试使用依赖项注入框架。我没有使用过Spark,所以我不确定哪种解决方案有效,哪种更好。是不是只有我一个人,但那充满了全局变量的味道?你会建议我把怪物重构成不同的类实例,并从依赖于这些“环境”变量的对象中取出东西吗?@Ian,是的。(从技术上讲,它更像全局常量:值只设置一次)。但是使用Shared
singleton已经有点像使用全局状态了。这个答案只是为了让她相信