When would you use extensions or a builder pattern...
# codingconventions
m
When would you use extensions or a builder pattern? The following code snippet in the 🧵
1️⃣
Copy code
object Filter {
    private val filters = StringBuilder()

    private fun append(value: String?) = apply {
        if (filters.isNotBlank()) filters.append(" and ")
        if (!value.isNullOrBlank()) filters.append(value)
    }

    fun jobCategoryId(value: String?) = apply {
        append("job_category_id" eq value)
    }

    fun isFeatured(value: Boolean) = apply {
        append("is_featured" eq "$value")
    }

    override fun toString(): String = filters.toString()
}

// caller:
val filter = Filter
      .jobCategoryId("job-1")
      isFeatured(true)
2️⃣
Copy code
object Filter {
    private val filters = StringBuilder()

    private fun append(value: String?) = apply {
        if (filters.isNotBlank()) filters.append(" and ")
        if (!value.isNullOrBlank()) filters.append(value)
    }
    
    override fun toString(): String = filters.toString()
}

fun Filter.jobCategoryId(value: String?) = apply {
    append("job_category_id" eq value)
}

fun Filter.isFeatured(value: Boolean) = apply {
    append("is_featured" eq "$value")
}

// caller:
import com.company.filter.jobCategoryId
import com.company.filter.isFeatured

val filter = Filter
      .jobCategoryId("job-1")
      .isFeatured(true)
Any suggestion to the above code? Using an
object
to have a similar behavior as static class, so we can directly calls the class without having to instantiate first, e.g:
Copy code
val filter = Filter() // instantiation
     .x()

// vs
val filter = Filter
     .x()
cmiiw
p
in my experience, a DSL is more 'idiomatic' than a fluent builder style, though builders are nicer for interop from Java. There's lots of options, but you could use functions or properties inside a
FilterBuilder
like this. It can get harder to indicate required vs optional stuff, but that's a problem with builders too
🙏 1
today i learned 1
m
to indicate required vs optional
Any suggestions which pattern that’ll be convenience about this parts?
p
if it's a minimal number of arguments, you can make them arguments for your filter constructor - that's (in my opinion, at least) the best way to guarantee they're present at compile time. You can make that work with either approach you outlined or the one I mentioned. If you have many required properties, then I'm not aware of any ideal pattern. Kotlin's named + default arguments are a big boost to readability there, and can make the fluent builder style totally unnecessary
🙏 1
today i learned 1