IT story

Kotlin에서 빌더 패턴을 구현하는 방법은 무엇입니까?

hot-time 2020. 7. 17. 07:58
반응형

Kotlin에서 빌더 패턴을 구현하는 방법은 무엇입니까?


안녕하세요 저는 Kotlin 세계의 초보자입니다. 나는 지금까지 본 것을 좋아하고 응용 프로그램에서 사용하는 일부 라이브러리를 Java에서 Kotlin으로 변환하려고 생각하기 시작했습니다.

이 라이브러리는 setter, getter 및 Builder 클래스가있는 Pojo로 가득합니다. 이제 Kotlin에서 빌더를 구현하는 가장 좋은 방법은 무엇인지 알아 보았지만 성공하지 못했습니다.

두 번째 업데이트 : 문제는 Kotlin에서 일부 매개 변수를 사용하여 간단한 pojo에 대한 빌더 디자인 패턴을 작성하는 방법입니다. 아래 코드는 Java 코드를 작성한 다음 eclipse-kotlin-plugin을 사용하여 Kotlin으로 변환하여 시도한 것입니다.

class Car private constructor(builder:Car.Builder) {
    var model:String? = null
    var year:Int = 0
    init {
        this.model = builder.model
        this.year = builder.year
    }
    companion object Builder {
        var model:String? = null
        private set

        var year:Int = 0
        private set

        fun model(model:String):Builder {
            this.model = model
            return this
        }
        fun year(year:Int):Builder {
            this.year = year
            return this
        }
        fun build():Car {
            val car = Car(this)
            return car
        }
    }
}

무엇보다도 기본적으로 명명 된 인수가 있기 때문에 대부분의 경우 Kotlin에서 빌더를 사용할 필요가 없습니다. 이것은 당신이 쓸 수 있습니다

class Car(val model: String? = null, val year: Int = 0)

다음과 같이 사용하십시오.

val car = Car(model = "X")

빌더를 절대적으로 사용하려면 다음과 같이하십시오.

S는 싱글 톤 companion object이기 때문에 Builder를 만드는 것은 의미가 없습니다 object. 대신 중첩 클래스로 선언하십시오 (Kotlin에서는 기본적으로 정적입니다).

객체를 규칙적으로 인스턴스화 할 수 있도록 속성을 생성자로 이동하고 (생성하지 않아야하는 경우 생성자를 비공개로 설정) 빌더를 사용하여 기본 생성자로 위임하는 보조 생성자를 사용하십시오. 코드는 다음과 같습니다.

class Car( //add private constructor if necessary
        val model: String?,
        val year: Int
) {

    private constructor(builder: Builder) : this(builder.model, builder.year)

    class Builder {
        var model: String? = null
            private set

        var year: Int = 0
            private set

        fun model(model: String) = apply { this.model = model }

        fun year(year: Int) = apply { this.year = year }

        fun build() = Car(this)
    }
}

용법: val car = Car.Builder().model("X").build()

빌더 DSL 을 사용하여이 코드를 추가로 단축 할 수 있습니다 .

class Car (
        val model: String?,
        val year: Int
) {

    private constructor(builder: Builder) : this(builder.model, builder.year)

    companion object {
        inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
    }

    class Builder {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}

용법: val car = Car.build { model = "X" }

일부 값이 필요하고 기본값이없는 경우 빌더의 생성자 및 build방금 정의한 메소드에 값을 입력해야합니다 .

class Car (
        val model: String?,
        val year: Int,
        val required: String
) {

    private constructor(builder: Builder) : this(builder.model, builder.year, builder.required)

    companion object {
        inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build()
    }

    class Builder(
            val required: String
    ) {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}

용법: val car = Car.build(required = "requiredValue") { model = "X" }


JSON에서 객체를 구문 분석하기 위해 Jackson 라이브러리를 사용하고 있기 때문에 빈 생성자가 필요하며 선택적 필드를 가질 수 없습니다. 또한 모든 필드는 변경 가능해야합니다. 그런 다음 빌더 패턴과 동일한 기능을하는이 멋진 구문을 사용할 수 있습니다.

val car = Car().apply{ model = "Ford"; year = 2000 }

나는 개인적으로 Kotlin에서 건축업자를 본 적이 없지만 아마도 나일 것입니다.

init블록 에서 필요한 모든 유효성 검사가 발생합니다 .

class Car(val model: String,
          val year: Int = 2000) {

    init {
        if(year < 1900) throw Exception("...")
    }
}

여기 당신이 정말로 원하는하지 않는 것이 생각하는 자유를했다 modelyear변경 될 수 있습니다. 또한 그 디폴트 값은 아무 의미가없는 것 같다 (특히 null대한 name)하지만 난 데모 용으로 하나 떠났다.

의견 : 명명 된 매개 변수없이 살기위한 수단으로 Java에서 사용되는 빌더 패턴. Kotlin 또는 Python과 같은 명명 된 매개 변수가있는 언어의 경우 매개 변수가 긴 (선택적) 매개 변수 목록이있는 생성자를 사용하는 것이 좋습니다.


한 가지 방법은 다음과 같은 작업을 수행하는 것입니다.

class Car(
  val model: String?,
  val color: String?,
  val type: String?) {

    data class Builder(
      var model: String? = null,
      var color: String? = null,
      var type: String? = null) {

        fun model(model: String) = apply { this.model = model }
        fun color(color: String) = apply { this.color = color }
        fun type(type: String) = apply { this.type = type }
        fun build() = Car(model, color, type)
    }
}

사용 샘플 :

val car = Car.Builder()
  .model("Ford Focus")
  .color("Black")
  .type("Type")
  .build()

추가 재미를 빌더로 선언하는 많은 예제를 보았습니다. 나는 개인적으로이 접근법을 좋아한다. 빌더를 작성하는 노력을 절약하십시오.

package android.zeroarst.lab.koltinlab

import kotlin.properties.Delegates

class Lab {
    companion object {
        @JvmStatic fun main(args: Array<String>) {

            val roy = Person {
                name = "Roy"
                age = 33
                height = 173
                single = true
                car {
                    brand = "Tesla"
                    model = "Model X"
                    year = 2017
                }
                car {
                    brand = "Tesla"
                    model = "Model S"
                    year = 2018
                }
            }

            println(roy)
        }

        class Person() {
            constructor(init: Person.() -> Unit) : this() {
                this.init()
            }

            var name: String by Delegates.notNull()
            var age: Int by Delegates.notNull()
            var height: Int by Delegates.notNull()
            var single: Boolean by Delegates.notNull()
            val cars: MutableList<Car> by lazy { arrayListOf<Car>() }

            override fun toString(): String {
                return "name=$name, age=$age, " +
                        "height=$height, " +
                        "single=${when (single) {
                            true -> "looking for a girl friend T___T"
                            false -> "Happy!!"
                        }}\nCars: $cars"
            }
        }

        class Car() {

            var brand: String by Delegates.notNull()
            var model: String by Delegates.notNull()
            var year: Int by Delegates.notNull()

            override fun toString(): String {
                return "(brand=$brand, model=$model, year=$year)"
            }
        }

        fun Person.car(init: Car.() -> Unit): Unit {
            cars.add(Car().apply(init))
        }

    }
}

아직 예외를 throw하는 대신 오류를 표시하는 것처럼 일부 필드를 DSL에서 초기화 할 수있는 방법을 아직 찾지 못했습니다. 아는 사람이 있으면 알려주세요.


간단한 수업의 경우 별도의 빌더가 필요하지 않습니다. Kirill Rakhman이 설명한대로 선택적 생성자 인수를 사용할 수 있습니다.

더 복잡한 수업이있는 경우 Kotlin은 Groovy 스타일 빌더 / DSL을 작성하는 방법을 제공합니다.

유형 안전 빌더

예를 들면 다음과 같습니다.

Github 예제-빌더 / 어셈블러


요즘 사람들은 Kotlin의 Type-Safe Builders를 확인해야합니다 .

위에서 언급 한 객체 생성 방식은 다음과 같습니다.

html {
    head {
        title {+"XML encoding with Kotlin"}
    }
    // ...
}

유용한 '실제'사용 예는 유형 안전 빌더를 사용하여 뷰와 컴포넌트조립 하는 vaadin-on-kotlin 프레임 워크 입니다.


나는 파티에 늦었다. 프로젝트에서 Builder 패턴을 사용해야한다면 같은 딜레마가 발생했습니다. 나중에, 연구 후에 Kotlin은 이미 명명 된 인수와 기본 인수를 제공하기 때문에 절대적으로 불필요하다는 것을 깨달았습니다.

If you really need to implement, Kirill Rakhman's answer is solid answer on how to implement in most effective way. Another thing you may find it useful is https://www.baeldung.com/kotlin-builder-pattern you can compare and contrast with Java and Kotlin on their implementation


I would say the pattern and implementation stays pretty much the same in Kotlin. You can sometimes skip it thanks to default values, but for more complicated object creation, builders are still a useful tool that can't be omitted.


you can use optional parameter in kotlin example:

fun myFunc(p1: String, p2: Int = -1, p3: Long = -1, p4: String = "default") {
    System.out.printf("parameter %s %d %d %s\n", p1, p2, p3, p4)
}

then

myFunc("a")
myFunc("a", 1)
myFunc("a", 1, 2)
myFunc("a", 1, 2, "b")

class Foo private constructor(@DrawableRes requiredImageRes: Int, optionalTitle: String?) {

    @DrawableRes
    @get:DrawableRes
    val requiredImageRes: Int

    val optionalTitle: String?

    init {
        this.requiredImageRes = requiredImageRes
        this.requiredImageRes = optionalTitle
    }

    class Builder {

        @DrawableRes
        private var requiredImageRes: Int = -1

        private var optionalTitle: String? = null

        fun requiredImageRes(@DrawableRes imageRes: Int): Builder {
            this.intent = intent
            return this
        } 

        fun optionalTitle(title: String): Builder {
            this.optionalTitle = title
            return this
        }

        fun build(): Foo {
            if(requiredImageRes == -1) {
                throw IllegalStateException("No image res provided")
            }
            return Foo(this.requiredImageRes, this.optionalTitle)
        }

    }

}

I implemented a basic Builder pattern in Kotlin with the follow code:

data class DialogMessage(
        var title: String = "",
        var message: String = ""
) {


    class Builder( context: Context){


        private var context: Context = context
        private var title: String = ""
        private var message: String = ""

        fun title( title : String) = apply { this.title = title }

        fun message( message : String ) = apply { this.message = message  }    

        fun build() = KeyoDialogMessage(
                title,
                message
        )

    }

    private lateinit var  dialog : Dialog

    fun show(){
        this.dialog= Dialog(context)
        .
        .
        .
        dialog.show()

    }

    fun hide(){
        if( this.dialog != null){
            this.dialog.dismiss()
        }
    }
}

And finally

Java:

new DialogMessage.Builder( context )
       .title("Title")
       .message("Message")
       .build()
       .show();

Kotlin:

DialogMessage.Builder( context )
       .title("Title")
       .message("")
       .build()
       .show()

I was working on a Kotlin project that exposed an API consumed by Java clients (which can't take advantage of the Kotlin language constructs). We had to add builders to make them usable in Java, so I created an @Builder annotation: https://github.com/ThinkingLogic/kotlin-builder-annotation - it's basically a replacement for the Lombok @Builder annotation for Kotlin.

참고URL : https://stackoverflow.com/questions/36140791/how-to-implement-builder-pattern-in-kotlin

반응형