Menu
Coddy logo textTech

ファクトリーパターン

CoddyのSwiftジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 43/57。

ファクトリパターンは、オブジェクトの生成を1か所にまとめます。呼び出し側は、多くの場合名前や型で必要なものを要求し、どの具象型が生成されたかを知らなくても、すぐに使えるオブジェクトを受け取ります。

protocol Shape { func area() -> Double }
struct Square: Shape { let side: Double; func area() -> Double { side * side } }
struct Circle: Shape { let r: Double; func area() -> Double { 3 * r * r } }

enum ShapeFactory {
    static func make(_ kind: String, size: Double) -> (any Shape)? {
        switch kind {
        case "square": return Square(side: size)
        case "circle": return Circle(r: size)
        default: return nil
        }
    }
}

print(ShapeFactory.make("square", size: 3)!.area())
print(ShapeFactory.make("hexagon", size: 3) == nil)

出力:

9.0
true

呼び出し側はプロトコルのみに依存します。新しい図形を追加する場合、変更するのはファクトリであり、図形を作成するすべての場所ではありません。

protocol Notifier { func send(_ m: String) -> String }
struct Email: Notifier { func send(_ m: String) -> String { "email: \(m)" } }
struct SMS: Notifier { func send(_ m: String) -> String { "sms: \(m)" } }

func makeNotifier(for user: String) -> any Notifier {
    user.hasSuffix("@mail.com") ? Email() : SMS()
}

print(makeNotifier(for: "ada@mail.com").send("hi"))
print(makeNotifier(for: "555-0100").send("hi"))

出力:

email: hi
sms: hi

型自身に定義する静的ファクトリメソッドは、このパターンをより軽量にした形式です。値を構築する一般的な方法に名前を付けられます:

struct Color {
    let r: Int, g: Int, b: Int
    static func gray(_ v: Int) -> Color { Color(r: v, g: v, b: v) }
    static let white = Color(r: 255, g: 255, b: 255)
}

print(Color.gray(100).g)
print(Color.white.b)

出力:

100
255

ShapeFactoryのようなケースを持たないenumは、ファクトリ関数をグループ化する一般的な方法です。インスタンス化できないため、純粋に名前空間として機能します。

enum ShapeFactory {        // ケースなし: 作成できない
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

チャレンジ

簡単

料金プラン用のファクトリを作成してください。プロトコル Plan には name と price(forUsers:) -> Int があります。Free(料金は0。ただし3ユーザーまでで、それを超える場合の料金は-1で、利用不可を意味します)、Team(ユーザー1人あたり10)、Enterprise(一律500、さらに100人を超えるユーザー1人あたり5)を実装してください。free、team、enterprise の場合はプランを返し、それ以外の場合は nil を返す PlanFactory.make(_:) を完成させてください。

提供されたコードは plan,users 形式の行を読み取り、team for 12 users: 120、free does not allow 5 users、または unknown plan: gold を出力します。

コードは Plan.swift、Free.swift、Team.swift、Enterprise.swift、PlanFactory.swift に記述してください。main.swift には提供された入力・出力コードが含まれており、編集できません。

自分で試してみよう

// 提供された入出力コード: そのままにしておく
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",").map(String.init)
    let users = Int(p[1])!
    guard let plan = PlanFactory.make(p[0]) else {
        print("unknown plan: \(p[0])")
        continue
    }
    let price = plan.price(forUsers: users)
    print(price < 0 ? "\(plan.name) does not allow \(users) users" : "\(plan.name) for \(users) users: \(price)")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Swiftプレイグラウンド