The `init` question made me wonder if its okay to ...
# getting-started
a
The
init
question made me wonder if its okay to do the builder pattern as
Copy code
class MyClassBuilder(init: MyClass.() -> Unit) {
    private val myClass: Myclass
    init {
        myClass = MyClass().init()
    }
    
    fun build() = myClass
}
🗿 1
m
Kotlin provides an ability to use named parameters which should cover most cases you have for standard builders from Java. Additionally, you have an ability to use value classes in cases you have a bunch string parameters or whatever. In case you do need to formalise a Builder, your approach is definitely not the best as clients of your builder will have to rely on dsl-like syntax which means no type safety, ability to lose configuration and additional mental load for those who are not yet familiar with something they are building. Another thing is that your builder eagerly constructs and configures its subject despite the fact there was no call to build yet. If you need a builder, make use of companion object for your Builder and make sure the configuration is lazy. You probably want it to be type safe as well.
1
a
Does this make it any better?
Copy code
class MyClassBuilder(init: MyClass.() -> Unit) {
    private val builder: MyClass.() -> Unit
    init {
        builder = init
    }
    
    fun build() = MyClass().builder()
}
It addresses the lazy, its still type safe(?), makes the attributes of MyClass configureable. No need for constructors in MyClass as the fields can be private and expose public methods/fields. Wouldn't a dsl-like syntax nicer/ more readable?
p
Given in this example
MyClass
looks to be mutable, how is this any better than
MyClass().apply { ...}
?
1
a
MyClass can have a private constructor? Honestly idk, the syntactic sugar of Kotlin confuses me on how to approach design patterns
m
Kotlin provides capabilities not syntax sugar
2
a
I think I understand the reasoning behind it. I just looked at the
<T> T.apply(block: T.() -> Unit): T
and its the same thing in principal. Defeats the purpose of
MyClassBuilder(init: MyClass.() -> Unit)
something like this altogether. And yes, a named arg constructor makes more sense. How would you approach building A that has B as a dependency. I can see it getting ugly pretty fast.
Copy code
Class B(val id: Int = 0)

Class A(val fName: String = "John", val lName: String = "Doe", val class: B)
m
The question is way too abstract. You can potentially use dependency injection frameworks to avoid initialising classes by hand.
👍 1
a
I'm mainly curious about the case where I would have to do it by hand, e.g. multiple
data classes
may need to be passed in some object after network/disk IO. If it becomes quite large, is it still okay to use a named arg? Or is there a different approach? e.g.
Copy code
Class Child(b: B, c: C, d: D, e: E, f: F, g: G, h: H): Base (b = b, c = c, d = d, e = e, f = f, g = g, h = h)
m
You can give them proper names and structure them vertically
d
Rather than have the data classes responsible for the network/disk IO, have another class or function that does that IO and then returns the fully constructed object.
1
a
Yes, I agree Daniel. That makes sense.