ソフトウェア設計

SwiftのBuilder Patternで引数8個の初期化地獄から抜け出す

Swiftでアプリを作っていると、ある時点で初期化コードがこんな形になっています。

読了 5 分
SwiftのBuilder Patternで引数8個の初期化地獄から抜け出すのカバー画像

Swiftでアプリを作っていると、ある時点で初期化コードがこんな形になっています。

User(name:age:email:phone:address:isVerified:profileURL:createdAt:)

括弧の中に引数が8個。

呼び出し側を見ると、どの値がemailでどれがphoneなのか、目で一つずつ数えることになります。

サイドプロジェクトでもよくある状況ですが、SwiftのBuilder Patternで整理すればすっきり解決できます。

結論から言うと、引数の多い初期化はBuilder Patternで「1行に1つずつ」設定する形に変えると、ずっと読みやすくなります。順番を覚える必要も、使わない値を無理にnilで埋める必要もありません。

この記事で得られる内容を、先にまとめておきます。

  1. 引数8個のinitがなぜミスを招くのか
  2. Builder Patternがどう解決するのか
  3. Swiftで実際に使うコードの形
  4. このパターンをいつ使い、いつ避けるべきか

引数8個のinitは、なぜ地獄なのか?

引数が3〜4個なら、実際のところ大きな問題はありません。

問題は、数が増えてから始まります。

1つ目は順番のミスです。emailの位置にphoneを渡しても、どちらもStringならコンパイラーは検出できません。

2つ目は、オプション値の扱いが汚くなることです。今は使わない値までnil, nil, nilで埋める必要があります。

3つ目は、呼び出し側が長くなり、何のオブジェクトを作っているのか一目で分からなくなることです。

引数が増えるほどinitは「埋めるべき空欄だらけのテスト用紙」になり、空欄が増えるほどミスも増えていきます。

phoneとprofileURLの位置を入れ替えるバグで、1日以上つぶれることもあります。型が同じだと、このようなミスは本当に見つけにくいものです。


Builder Patternとは?

Builder Patternは、複雑なオブジェクトを一度に作らず、値を一つずつ設定して最後に完成させる方法です。

初期化時にすべての値を渡すのではなく、必要な値だけを選んで設定し、build()のようなメソッドで最終的なオブジェクトを受け取る構造です。

レストランで注文するときを思い浮かべると簡単です。材料を一度に叫ぶのではなく、「パンは全粒粉、チーズは追加、ソースは抜きで」と項目ごとに伝えます。

重要な値を設定するメソッドが自身を返せば、ドットをつなげるようにチェーンできます。以下は最もシンプルなBuilderです。

final class UserBuilder {
    private var name = ""
    private var email: String?
    // 値を設定し、 self返すことでチェーンを可能にする
    func setName(_ v: String) -> Self { name = v; return self }
    func setEmail(_ v: String) -> Self { email = v; return self }
    func build() -> User { User(name: name, email: email) }
}

設定メソッドがSelfを返すため、.setName(...).setEmail(...)のようにつなげられます。

チェーン呼び出しは実際にはこのように流れます
チェーン呼び出しは実際にはこのように流れます
Builderを1つ追加して、コーヒーを飲みながら整理したら気持ちが楽になりました
Builderを1つ追加して、コーヒーを飲みながら整理したら気持ちが楽になりました

SwiftでBuilder Patternをどう使う?

実際の呼び出し側がどう変わるのかが、いちばん気になるところでしょう。

先ほど作ったBuilderを使うと、オブジェクト生成コードは次のように変わります。

// 必要な値だけをラベル付きで設定し、順番を気にしない
let user = UserBuilder()
    .setName("イ・ソクウ")
    .setEmail("[email protected]")
    .build()
// 使わない値に nilを埋める必要がない

見てのとおり、順番を気にする必要はありません。メソッド名自体がラベルになるため、emailの位置にphoneを渡すミスを構造的に防げます。

使わない値は、そのメソッドを呼ばなければ済みます。

Swiftでは、classではなくstructでBuilderを作ったり、クロージャを渡して1つのブロック内で設定したりする方法もよく使います。プロジェクトのスタイルに合わせて選んでください。

ラベル付きメソッドでつなげると順番の心配がなくなります
ラベル付きメソッドでつなげると順番の心配がなくなります

Builder Patternをいつ使い、いつ避けるか

よさそうだからとどこにでも入れると、かえってコードが増えるだけです。

私の基準を表にまとめました(2026年時点の個人プロジェクトでの経験に基づきます)。

状況 おすすめ
引数5個以上、オプション値が多い Builder Pattern
同じ型の引数が複数ある Builder Pattern
引数2〜3個、すべて必須 通常のinit
値が単純で固定されている 通常のinit

まとめると、引数が少なくすべて必須なら、わざわざBuilderを作る理由はありません。

逆に、オプション値が混在する大きなオブジェクトなら、Builderのほうが確実に便利です。

Q. Builderを作るとコードがさらに増えませんか?

そのとおりです。Builderクラス自体のコードは増えます。ただ、そのオブジェクトを複数箇所で作るなら、呼び出し側がきれいになるメリットのほうが大きくなります。

Q. Swiftにはデフォルト引数がありますが、それではだめですか?

引数が3〜4個なら、デフォルト引数だけで十分です。数が多く組み合わせも多様なときに、Builderのメリットが生きます。


引数8個のinitに悩んでいるなら、その箇所だけBuilder Patternを試してみてください。すべてを作り直す必要はなく、最も複雑なオブジェクト1つから始めれば負担も少なく済みます。今日も楽しいコーディングを!

あわせて読みたい