Search code examples
kotlinfactory-patternbuilder-pattern

Is it possible to verify at compile time whether the required function is called for the Factory Class in Kotlin?


 class ModelFactory {


    fun setA() : ModelFactory {
       // blabla...
    }

    fun setB() : ModelFactory {
      // blabla...
    }

    fun setC() : ModelFactory {
      // blabla...
    }

    fun build() : Model {
      // An error occurs if any of setA, setB, and setC is not called.
    }
}


//example

fun successTest() {
   ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}

fun failTest() {
   ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}

It's awkward grammatically, but I think it's been expressed what I want.

I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.

If possible, I think I should use annotations. But is this really possible at compile time?


Solution

  • With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.

    If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.

    class Model(val a: String = "", val b: String = "", val c: String = "")
    
    class StepBuilder {
        companion object {
            fun builder(): AStep = Steps()
        }
    
        interface AStep {
            fun setA(a: String): BStep
        }
    
        interface BStep {
            fun setB(b: String): CStep
        }
    
        interface CStep {
            fun setC(c: String): BuildStep
        }
    
        interface BuildStep {
            //fun setOptionalField(x: String): BuildStep
            fun build(): Model
        }
    
        class Steps : AStep, BStep, CStep, BuildStep {
            private lateinit var a: String
            private lateinit var b: String
            private lateinit var c: String
            override fun setA(a: String): BStep {
                this.a = a
                return this
            }
    
            override fun setB(b: String): CStep {
                this.b = b
                return this
            }
    
            override fun setC(c: String): BuildStep {
                this.c = c
                return this
            }
    
            override fun build() = Model(a, b , c)
        }
    }
    
    fun main() {
        // cannot build until you call all three setters
        val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
    }