0

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.

  1. With this approach, buildInternal signature will change with any new Factlog specific parameter, so the solution will be to place the computation of sourceTable in ConfigBuilder, outside the method build
  2. If I do this, I am forced to generate sourceTable before the call to validate
  3. 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 method extractQuery

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.

alexlipa
  • 1,131
  • 2
  • 12
  • 27
  • just have `bulidInternal`call `extractFrom` directly rather than taking the result as an argument – Dima Aug 01 '18 at 11:25
  • so this means that `extractFrom` call should be copied everywhere, in every sub-iimplementation – alexlipa Aug 03 '18 at 10:51
  • I don't understand what you are trying to achieve ... If parameters `extractFrom` computes are specific to subclasses, then, of course, you have to call it in each subclass. If they are not, then I don't see what's the problem with your original approach. – Dima Aug 03 '18 at 13:07

0 Answers0