Scala特质混入(trait mixins)用于对象/类型安全配置

4

这是一个面向对象的设计问题。我在我的应用程序中使用类型安全的Config。Config接口非常有用,但是我的应用程序配置文件中有一些必填字段。我想做的是创建一个Config的子接口,并添加这两个顶层方法。类似于这样:

trait AppConfig extends Config{
    def param1:String 
    def param2:String 
}

然而,如果给定Config的实例创建一个真正的AppConfig似乎不可行。(我不想创建包装对象并复制Config接口上的所有方法)。理想情况下,我正在寻找能够实现类似于此的东西。

val conf:Config = //get config object from somewhere
return conf with AppConfig { overrider def param1 = {"blah"} }

我知道最后一行是无效的。但我正在寻找一个具有相同功能的模式/结构。


在Groovy中,您可以使用@Delegate来实现此功能。 - sourcedelica
3个回答

2
我们一直在使用Configrity来处理这类事情。以下是一个例子:
我们将编译的默认值保存在对象中,以便用于单元测试等。
object DefaultConfigs {
  val defaultConfig = Configuration(
    "param1" -> "blah"
  )

  val otherConfig = Configuration(
    "param2" -> "blah"
  )

  val appConfig = defaultConfig.include(otherConfig)
}

然后在运行时,我们可以选择包含它们或不包含它们。
val appConfig = Configuration.load(pathToConfig) include DefaultConfigs.appConfig

1
如何使用Dynamic和Reflection的组合。Dynamic用于处理方便方法,Reflection用于处理config上的方法。
以下是一种尝试:
class ConfigDynamic(val config: Config) extends Dynamic {
  def selectDynamic(name: String)= {
    name match {
      case "field1" => 
        config.getString("field1")
      case x @ _ =>
        // overly simplified here
        val meth = configClass.getDeclaredMethod(x)
        meth.invoke(config)
    }
  }
}

这看起来很有趣。让我试一下,然后回来。 - questionersam

1

你所需要的基本上是一种被称为“动态混入”的东西。这在Scala中不是开箱即用的。 有人开发了一个编译器插件来支持它:http://www.artima.com/weblogs/viewpost.jsp?thread=275588

然而,它有点过时,而且该项目似乎不再活跃。

更好的选择是使用Scala宏实现此功能(需要Scala 2.10,目前还没有稳定版本)。

但在你的情况下,所有这些可能都有点过度了。在某个经过充分测试的库可用之前,我建议手动创建代理(尽管看起来很乏味):

trait ConfigProxy extends Config {
  def impl: Config
  // Forward to the inner config
  def root: ConfigObject = impl.root
  def origin: ConfigOrigin = impl.origin
  //... and so on
}

val conf:Config = //get config object from somewhere
val myConf: AppConfig = new AppConfig with ConfigProxy { 
  val impl = conf
  val param1:String = "foo"
  val param2:String = "bar"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接