1. 序章

このクイック記事では、Kotlinでビルダーデザインパターンを実装する方法を説明します。

2. ビルダーパターン

Builderパターンは、人々がよく使用するパターンですが、自分で作成することはめったにありません。

多くのパラメータを含む可能性のあるオブジェクトの構築を処理するのは素晴らしいことであり、構築が完了したらオブジェクトを不変にしたい場合です。

詳細については、作成デザインパターンこちらに関するチュートリアルをご覧ください。

3. 実装

Kotlinは、名前付きパラメーターやデフォルトパラメーター、 apply()データクラスなど、従来のBuilderパターン実装の使用を回避する多くの便利な機能を提供します。

そのため、最初に従来のJavaスタイルの実装を示し、次にKotlinスタイルの短い形式を示します。

3.1. Javaスタイルの実装

外部オブジェクトが直接アクセスすることを望まないため、読み取り専用フィールドを含む1つのクラス FoodOrderの作成を開始しましょう。

class FoodOrder private constructor(builder: FoodOrder.Builder) {

    val bread: String?
    val condiments: String?
    val meat: String?
    val fish: String?

    init {
        this.bread = builder.bread
        this.condiments = builder.condiments
        this.meat = builder.meat
        this.fish = builder.fish
    }

    class Builder {
        // builder code
    }
}

コンストラクターはプライベートであるため、ネストされたBuilderクラスのみがコンストラクターにアクセスできます。

次に、オブジェクトの構築に使用されるネストされたクラスの作成に移りましょう。

class Builder {

    var bread: String? = null
      private set
    var condiments: String? = null
      private set
    var meat: String? = null
      private set
    var fish: String? = null
      private set

    fun bread(bread: String) = apply { this.bread = bread }
    fun condiments(condiments: String) = apply { this.condiments = condiments }
    fun meat(meat: String) = apply { this.meat = meat }
    fun fish(fish: String) = apply { this.fish = fish }
    fun build() = FoodOrder(this)
}

ご覧のとおり、 Builderには、外部クラスと同じフィールドがあります。 外側のフィールドごとに、マッチングセッターメソッドがあります。

セッターメソッドを使用する代わりに、1つ以上の必須フィールドがある場合は、コンストラクターにそれらを設定させましょう。

流暢なデザインアプローチをサポートするために、apply関数を使用していることに注意してください。

最後に、 build メソッドを使用して、FoodOrderコンストラクターを呼び出します。

3.2. Kotlinスタイルの実装

Kotlinを最大限に活用するには、Javaで慣れ親しんだいくつかのベストプラクティスを再検討する必要があります。 それらの多くは、より良い代替品に置き換えることができます。

慣用的なKotlinコードを作成する方法を見てみましょう。

class FoodOrder private constructor(
  val bread: String?,
  val condiments: String?,
  val meat: String?,
  val fish: String?) {

    data class Builder(
      var bread: String? = null,
      var condiments: String? = null,
      var meat: String? = null,
      var fish: String? = null) {

        fun bread(bread: String) = apply { this.bread = bread }
        fun condiments(condiments: String) = apply { this.condiments = condiments }
        fun meat(meat: String) = apply { this.meat = meat }
        fun fish(fish: String) = apply { this.fish = fish }
        fun build() = FoodOrder(bread, condiments, meat, fish)
    }
}

Kotlinには、オーバーロードの数を最小限に抑え、関数呼び出しの可読性を向上させるのに役立つ名前付きパラメーターとデフォルトパラメーターが付属しています。

また、別のチュートリアルここで詳しく説明しているKotlinのデータクラス構造を利用することもできます。

最後に、Javaスタイルの実装と同様に、 apply()は流暢なセッターの実装に役立ちます。

4. 使用例

簡単に説明すると、これらのBuilderパターンの実装を使用してFoodOrderオブジェクトを構築する方法を見てみましょう。

val foodOrder = FoodOrder.Builder()
  .bread("white bread")
  .meat("bacon")
  .condiments("olive oil")
  .build()

5. 結論

Builderパターンは、多くのコンストラクターを作成せずに不変オブジェクトを柔軟に作成する方法というオブジェクト指向プログラミングの非常に一般的な問題を解決します。

ビルダーを検討するときは、構造が複雑かどうかに焦点を当てる必要があります。 構築パターンが単純すぎる場合は、柔軟なビルダーオブジェクトを作成する努力がメリットをはるかに超える可能性があります。

いつものように、コードはGithubから入手できます。