ソフトウェア設計

Swiftのファクトリメソッドパターン:initではなく使う理由

Swiftのファクトリ関数は、生成の意図を示す名前を付け、返却型と再利用方針を柔軟にします。initではなくstatic funcを選ぶ基準と、GoFのファクトリメソッドとの違いを整理します。

読了 4 分
Swiftのファクトリメソッドパターン:initではなく使う理由のカバー画像

Swiftでアプリを作っていると、オブジェクト生成のコードを一日に何十回も書くことになります。

しかし、ある時点からinitだけでは窮屈に感じる場面が増えてきます。

最初は何でもinitで作りますが、プロジェクトが大きくなるとファクトリメソッドパターンを検討するようになります。

この記事では、Swiftのファクトリメソッドパターンとは何か、なぜinitではなくファクトリを使うのかを実際のコードとともに説明します。

ファクトリメソッドパターンを一言で言うと?

まず結論から説明します。

ファクトリメソッドは、オブジェクト生成の処理をinitの外にある別メソッドへ切り出し、名前を付け、返却型を自由に選べるようにする方法です。

Swiftでは通常、static funcで作る静的ファクトリメソッドを指します。

ここで扱うのは、タイプメソッドで生成処理を包む静的ファクトリです。サブクラスが生成する型を決めるGoFパターンとは異なり、その境界はファクトリメソッドと抽象ファクトリの違いで比較しています。

initは、言語が定めたルールの中で動きます。

名前を付けられず、必ず自分の型の新しいインスタンスを作らなければなりません。

ファクトリメソッドはこの制約を解消します。

簡単な例を見てみましょう。

struct Color {
    let r, g, b: Double
    // 生成の意図を名前で示す
    static func rgb(_ r: Double, _ g: Double, _ b: Double) -> Color {
        Color(r: r, g: g, b: b)
    }
    static func gray(_ v: Double) -> Color {
        Color(r: v, g: v, b: v)
    }
}

Color.gray(0.5)のように呼び出せば、灰色を生成することがコードだけで分かります。


initではなくファクトリを使う理由

私の経験から整理すると、主な理由は4つあります。

1つ目は、名前を付けられることです。

initはすべて同じ名前なので、パラメータだけで区別する必要があります。

同じDoubleを2つ受け取るイニシャライザが複数あると、混乱しやすくなります。

ファクトリメソッドなら、from(hex:)rgb(_:_:_:)のように意図を名前に込められます。

2つ目は、毎回新しいインスタンスを作らなくてよいことです。

initは常に新しいオブジェクトを返しますが、ファクトリはキャッシュ値やシングルトンを返せます。

3つ目は、返却型を柔軟に選べることです。

状況に応じて、サブタイプや別の実装を返せます。

4つ目は、失敗処理をより明確に表現できることです。

static funcを1行加えるだけでコードがぐっと読みやすくなる
static funcを1行加えるだけでコードがぐっと読みやすくなる

以下はキャッシュを利用するファクトリの例です。

final class IconCache {
    private static var store: [String: Icon] = [:]
    // 同じ名前なら、作成済みのアイコンを再利用する
    static func icon(named name: String) -> Icon {
        if let cached = store[name] { return cached }
        let icon = Icon(name: name)
        store[name] = icon
        return icon
    }
}

同じアイコンを何度も呼び出すとき、メモリを節約できます。

あれば取り出し、なければ作ってキャッシュする
あれば取り出し、なければ作ってキャッシュする

では、もうinitは使わなくてよいのでしょうか?

いいえ、決してそうではありません。

ファクトリはinitの代わりではなく、initを包むための道具です。

ファクトリメソッドの中でも、最終的にはinitを呼び出してオブジェクトを作ります。

単純な値型や生成処理が明白な場合は、普通にinitを使う方がよいでしょう。

理由なくファクトリで包むと、コードが長くなり読みにくくなるだけです。

ファクトリが活きる場面は別にあります。

  • 生成方法が複数あり、名前で区別したいとき
  • キャッシュやプーリングなど、再利用が必要なとき
  • 複雑な初期化手順を1か所に隠したいとき
  • プロトコル型で返して実装を隠したいとき

これらに当てはまらなければ、initを使うのが正解です。

Swift標準ライブラリも、このように使い分けています。

Array(repeating:count:)はinitですが、UIColor.systemBlueのようなstaticプロパティも、実質的にはファクトリのいとこです。


実務で判断する基準のまとめ

迷ったときに私が使う基準を表にまとめました。

状況 おすすめ
単純な値の保存 init
生成方法が複数 ファクトリメソッド
インスタンスの再利用・キャッシュ ファクトリメソッド
実装を隠してプロトコルを返す ファクトリメソッド
初期化が1行で終わる init
迷ったときはいつもこの表を思い出して選びます
迷ったときはいつもこの表を思い出して選びます

まとめると、initは「どう作るか」を扱い、ファクトリは「何を、なぜ作るか」を扱います。

両者は競合するものではなく、役割の異なる相棒です。

最初はinitで始め、生成処理が複雑になった時点でファクトリへ移す流れをおすすめします。

今日紹介した基準だけ覚えておけば、コードはよりすっきりします。楽しいSwiftコーディングを!

あわせて読みたい