I want my client code to look somewhat like this:
val config:Config = new MyConfig("c:/etc/myConfig.txt")
println(config.param1)
println(config.param2)
println(config.param3)
Which means that:
- The Config interface defines the config fields
- MyConfig is a Config implementation -- all the wiring needed is the instantiation of the desired implementation
- Data is loaded lazily -- it should happen on first field reference (config.param1 in this case)
So, I want the client code to be friendly, with support for interchangeable implementations, with statically typed fields, hiding lazy loading. I also want it to be as simple as possible for making alternative implementations, so Config should somewhat guide you.
I am not satisfied with what I came up with so far:
trait Config {
lazy val param1:String = resolveParam1
lazy val param2:String = resolveParam2
lazy val param3:Int = resolveParam3
protected def resolveParam1:String
protected def resolveParam2:String
protected def resolveParam3:Int
}
class MyConfig(fileName:String) extends Config {
lazy val data:Map[String, Any] = readConfig
// some dummy impl here, should read from a file
protected def readConfig:Map[String,Any] = Map[String, Any]("p1" -> "abc", "p2" -> "defgh", "p3" -> 43)
protected def resolveParam1:String = data.get("p1").get.asInstanceOf[String]
protected def resolveParam2:String = data.get("p2").get.asInstanceOf[String]
protected def resolveParam3:Int = data.get("p3").get.asInstanceOf[Int]
}
I'm sure there are better solutions, that's where you can help :)
One thing I especially don't like here is that MyConfig defines an intermediate container with some arbitrary keys, and since it is Map[String, Any], I need to cast the values.