Menu
CoddyTech

工厂模式

本课属于 Coddy Swift 学习旅程的“面向对象编程”单元。第 43 课,共 57 课。

工厂模式将对象的创建集中在一处。调用方提出所需对象,通常通过名称或类型指定,并获得一个可直接使用的对象,而无需知道具体创建了哪种类型:

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 ShapeFactory {        // 没有枚举成员:无法创建实例
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

挑战

简单

编写一个用于定价方案的工厂。协议 Plan 包含 name 和 price(forUsers:) -> Int。实现 Free(价格为 0,且最多只允许 3 个用户:超过上限时价格为 -1,表示不允许)、Team(每位用户 10)和 Enterprise(固定收费 500,超过 100 位用户后,每位额外用户收费 5)。完成 PlanFactory.make(_:),当方案为 free、team 或 enterprise 时返回对应方案,否则返回 nil。

提供的代码会读取格式为 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 在线编辑器