This question is related to this one. I have a family of classes of type Config and all of them are built using a builder pattern. Therefore, I have also ConfigBuilder classes that form a hierarchy as well, since many implementation share the same behaviour.
What I want to achieve is that ConfigBuilder expose a method build
which always perform these steps: validate the parameters (throwing an exception if not valid) and build the Config. Of course I would like to do this with the least possible duplication of code. In fact, the build method can be ideally split in two parts: a common build of the parameters shared by all implementation of Config and a implementation-specific build for each Config.
This is an example of the superclasses
abstract class Config {
def name: String
def query: String
def sourceTable: String
}
abstract class ConfigBuilder {
// common variables are set through setters by the user, which finally calls build
def build = {
validate
val sourceTable = extractFrom(query) // private local method
// it will contain more fields, extracted from the ones set by the user
buildInternal(sourceTable)
}
def validate = {
if(name == null) throw new Exception() // and all common checks
}
abstract def buildInternal(s:String): Config
}
And this is an implementation
case class Factlog private (
name:String, query:String, sourceTable:String, description: String)
class FactlogBuilder extends ConfigBuilder {
// description is set through setters by the user
def validate = {
super.validate()
if(description == null) throw new Exception()
}
def buildInternal(s:String) =
Factlog(name,query,s,description)
}
This snippet of code works but I would like to understand if this is the best way to do implement the build
and buildInternal
method.
- With this approach,
buildInternal
signature will change with any new Factlog specific parameter, so the solution will be to place the computation ofsourceTable
in ConfigBuilder, outside the method build - If I do this, I am forced to generate
sourceTable
before the call to validate - As last approach, I could instantiate the variable outside as
var sourceTable = _
and then, after validate method call, give it the value returned by the methodextractQuery
I am tempted to use the approach 3, but I assume this is not really how Scala should be used. I am sure there are better approaches to compose these hierarchies.
P.S. the list of parameters will surely grow over time, so this is something that I have to consider. Moreover, the builder pattern usage is facilitated by a Spark feature that at the moment I cannot avoid to use.