Builder Pattern


これは複雑なオブジェクトを段階的に組織できるスキームを提案する設計モデルである.このモードでは、同じ構成コードを使用してオブジェクトの異なるタイプと表現を作成することもできます.

Problem


多くのフィールドとネストされたオブジェクトを困難なステップで初期化する必要があると仮定します.これらの複雑なコードは、クライアントコード全体に分散することもできます.たとえば、Houseオブジェクトを作成する場合は、基本的なウィンドウ、ドア、屋根、壁が必要になる場合があります.しかし場合によっては前庭のある家、車庫のある家もあります.これらの状況はどうしてすべて考慮することができますか?
最も簡単な方法は継承によって解決することです.サブクラスにHouseを拡張し、前庭のあるサブクラス、車庫のあるサブクラスを作成します.ただし、これを行うと、かなりの数のサブクラスが作成されます.また、拡張要素を追加するたびに、追加のサブクラスを作成する必要があります.
もう1つの方法は、必要なものをすべてHouseに配置し、構造関数を作成することです.この方法を使用すると、サブクラスの必要性が解消されます.しかし、ジェネレータ呼び出しが非常に醜くなり、前庭付きの家があまり使用されない場合、前庭フィールドは10回のうち9回を必要としないフィールドになります.

Solve


Builderモードは、独自のクラスからオブジェクト生成コードを抽出し、Builderという名前の別のオブジェクトに移動します.オブジェクトを生成するには、ジェネレータの一連のプロセスを実行するだけです.この場合、すべてのステップを呼び出す必要がないことに重点を置きます.必要なステップを呼び出すだけで、必要なオブジェクトに整理できます.
また、同じコンストラクションステップであっても、異なるコンストラクションクラスを異なる方法で作成することができます.次に、適切なコンストラクタを使用して異なるタイプのオブジェクトを作成します.
Directorという独立したクラスを使用して、コンストラクタステップの一連の呼び出しを抽出することもできます.Dircetorは構築ステップを実行する順序を定義し、Builderはこれらのステップの実装を提供します.もちろん、Directorクラスは必須クラスではありません.クライアントコードからプロパティ構築順序を直接呼び出すことができます.ただし、Directorクラスは、さまざまな構成インスタンスを含むプログラム全体で再利用するのに良い場所です.それだけでなく、クライアントコードに製品構成の詳細を完全に隠すことができるという利点があります.

Code


Builder

package builder

import java.time.LocalDate

interface Builder {
  fun seatCount(count: Int)
  fun carType(carType: CarType)
  fun engine(engine: Engine)
  fun dateOfManufacture(date: LocalDate)
}

CarBuilder

package builder

import java.time.LocalDate

class CarBuilder : Builder {
  var seatCount: Int? = null
  var carType: CarType? = null
  var engine: Engine? = null
  var dateOfManufacture: LocalDate? = null

  override fun seatCount(count: Int) {
    this.seatCount = count
  }

  override fun carType(carType: CarType) {
    this.carType = carType
  }

  override fun engine(engine: Engine) {
    this.engine = engine
  }

  override fun dateOfManufacture(date: LocalDate) {
    this.dateOfManufacture = date
  }

  fun build() = Car(seatCount, carType, engine, dateOfManufacture)
}

ManualBuilder

package builder

import java.time.LocalDate

class ManualBuilder : Builder {
  var seatCount: Int? = null
  var carType: CarType? = null
  var engine: Engine? = null
  var dateOfManufacture: LocalDate? = null

  override fun seatCount(count: Int) {
    this.seatCount = count
  }

  override fun carType(carType: CarType) {
    this.carType = carType
  }

  override fun engine(engine: Engine) {
    this.engine = engine
  }

  override fun dateOfManufacture(date: LocalDate) {
    this.dateOfManufacture = date
  }

  fun build() = Manual(seatCount, carType, engine, dateOfManufacture)
}

Car

package builder

import java.time.LocalDate

data class Car(val seatCount: Int?, val carType: CarType?, val engine: Engine?, val dateOfManufacture: LocalDate?)

Manual

package builder

import java.time.LocalDate

data class Manual(val seatCount: Int?, val carType: CarType?, val engine: Engine?, val dateOfManufacture: LocalDate?)

CarType

package builder

enum class CarType {
  SPORTS_CAR, SUV
}

Engine

package builder

data class Engine(val mileAge: Double, val volume: Double)

Director

package builder

import java.time.LocalDate

class Director {
  fun makeSportsCar(builder: Builder) {
    builder.seatCount(2)
    builder.carType(CarType.SPORTS_CAR)
    builder.engine(Engine(3.0, 0.0))
    builder.dateOfManufacture(LocalDate.of(1998, 2, 25))
  }

  fun makeSUV(builder: Builder) {
    builder.seatCount(4)
    builder.carType(CarType.SUV)
    builder.engine(Engine(2.5, 0.0))
    builder.dateOfManufacture(LocalDate.of(1998, 2, 25))
  }
}

Main

package builder

fun main() {
  println("which car is better?")
  println("1. SportsCar")
  println("2. SUV")

  val carBuilder = CarBuilder()
  val manualBuilder = ManualBuilder()
  val director = Director()

  when (readLine()) {
    "1" -> {
      director.makeSportsCar(carBuilder)
      director.makeSportsCar(manualBuilder)
    }
    "2" -> {
      director.makeSUV(carBuilder)
      director.makeSUV(manualBuilder)
    }
    else -> {
      println("Error!")
      return
    }
  }

  println("My Car is ${carBuilder.build()}")
  println("This Manual is ${manualBuilder.build()}")
}

こうぞう



適用性

  • は、多くのオプションパラメータを有するジェネレータにコンストラクタモードを適用することができる.アレイを使用して、ジェネレータに数十個のパラメータを挿入する必要はありません.
  • ジェネレータモードは、製品の様々な表現形態を構成し、他の類似のステップが含まれている場合にのみ適用される.基本コンストラクタインタフェースは、製品の特定の表現を構成するためにこれらのステップを実装するすべての可能な構成ステップを定義します.Directorブートビルダー.
  • ビルダーを使用して、製品を逐次構成できます.コンストラクタは、コンストラクションフェーズで未完了の製品を露出しません.すなわち,クライアントコードが不完全な結果を生じることを防止できる.
  • 欠点


    長所

  • オブジェクトステップ構成
  • 製品の各種表現を作成する際、同じ構成コード
  • を繰り返し使用する.
  • SRP=>複雑な構成コード
  • を製品のビジネスロジックから分離する.

    短所

  • モードの複数の新しいクラスを作成する->コードの全体的な複雑さ
  • を向上させる

    Git


    https://github.com/oh980225/DesignPattern/tree/main/src/main/kotlin/builder

    Ref


    https://refactoring.guru/design-patterns/builder