LoginSignup
1
2

More than 1 year has passed since last update.

Builder パターンについてまとめてみた【Swiftでデザインパターン攻略 #4】

Posted at

Builder パターン とは

概要

「Builder」を直訳すると「建築者」という意味です。
プログラミングにおいては、インスタンスの生成を担う「建築者」と捉える事ができます。

Wikipediaによると

オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする。

image.png
Wikipediaより

SwiftにおけるBuilder パターン

インスタンス生成時に、多くの引数をinitに渡すため、気づいたらコードが冗長化して見づらくなっているということが自分の中ではあるあるパターンです。

Swiftにおいては、デフォルト引数という仕組みがありますが、使いすぎるとインスタンス初期化時にゴチャゴチャします。
なので、そんな時は、Builderパターンの採用を考えてみてもいいかもと思います。

実装例

今回は、「とあるカフェで自分で組み合わせるオリジナルの飲み物をつくる」という状況を例にして考えます。

仕様は以下のようにします。
インスタンスを生成するときに、以下のパラメーターを渡してオリジナルの飲み物を作ります。

プロパティ 必須? デフォルト値
baseDrink コーヒー、紅茶、ジュース -
size S、M、L -
topping - なし、ナタデココ、タピオカ なし
inMilk - - false
ice - なし、バニラ、チョコ なし

最後に、生成されたインスタンス(オリジナルドリンク)の価格を計算します。

Sample Code

class OriginalDrinkBuilder {
    let baseDrink: BaseDrink
    let size: DrinkSize
    private(set) var topping: Topping = .none
    private(set) var inMilk = false
    private(set) var iceCream: IceCream = .none

    init(baseDrink: BaseDrink, size: DrinkSize) {
        self.baseDrink = baseDrink
        self.size = size
    }

    func addTopping(_ topping: Topping) -> Self {
        self.topping = topping
        return self
    }

    func setInMilk(_ flag: Bool) -> Self  {
        self.inMilk = flag
        return self
    }

    func addIceCream(_ flavor: IceCream) -> Self  {
        self.iceCream = flavor
        return self
    }

    func build() -> OriginalDrink {
        let drink = OriginalDrink(baseDrink: baseDrink, size: size)
        drink.topping = topping
        drink.inMilk = inMilk
        drink.iceCream = iceCream
        return drink
    }
}


class OriginalDrink {
    let baseDrink: BaseDrink
    let size: DrinkSize
    var topping: Topping = .none
    var inMilk = false
    var iceCream: IceCream = .none

    init(baseDrink: BaseDrink, size: DrinkSize) {
        self.baseDrink = baseDrink
        self.size = size
    }

    func account() -> Int {
        // NOTE: 会計
        return baseDrink.rawValue + size.rawValue + topping.rawValue + iceCream.rawValue + (inMilk ? 50 : 0)
    }
}

// used
let drink = OriginalDrinkBuilder(baseDrink: .coffee, size: .M)
    .addTopping(.tapioca)
    .setInMilk(true)
    .addIceCream(.vanilla)
    .build()

print("\(drink.account())円")

参考にしたサイト

実践で使うならきっとこんな感じだと思います。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2