1. ホーム
  2. design-patterns

[解決済み] KotlinでBuilderパターンを実装するには?

2022-04-13 22:03:48

質問

こんにちは、Kotlinの世界に入ったばかりの初心者です。私はこれまで見てきたものが好きで、私たちのアプリケーションで使用しているライブラリのいくつかをJavaからKotlinに変換することを考え始めました。

これらのライブラリは、セッター、ゲッター、ビルダークラスを持つPojosでいっぱいです。今、私はKotlinでBuilderを実装する最良の方法は何かを見つけるためにググっていますが、成功しません。

2回目の更新:問題は、Kotlinでいくつかのパラメータを持つ単純なpojoのためのBuilderデザインパターンをどのように書くかです。以下のコードは、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")

どうしてもビルダーを使いたい場合は、以下のような方法があります。

ビルダーを companion object は意味をなさない。 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" }