工厂模式
核心:
解耦创建逻辑 · 多种子类 · 条件分支创建
思想及适用场景:
主要用于封装对象的创建逻辑,调用方只关心"我要什么",不关心"怎么造"。
当需要根据条件创建不同子类时尤其有用。
角色
工厂模式是,一个工厂生产一种产品。
-
产品协议:定义产品能力、特点
-
产品:遵循协议的产品类/struct
-
工厂协议:定义生产方法
-
工厂:遵守协议,实现生产
示例
1、产品侧:创建 协议 及其 产品类(实现产品功能)
例:猫,狗
// ─── 产品协议 ───
protocol Animal {
var name: String { get }
func speak() -> String
}
// ─── 具体产品 ───
struct Dog: Animal {
let name = "小狗"
func speak() -> String { "汪汪!" }
}
struct Cat: Animal {
let name = "小猫"
func speak() -> String { "喵喵!" }
}
2、工厂侧:创建 协议 及其 工厂类(实现生产方法,创建对应产品)
例:猫工厂 生产猫;狗工厂 生产狗
// ─── 工厂协议(Creator) ───
protocol AnimalFactory {
func createAnimal() -> Animal // 工厂方法
}
// ─── 具体工厂 ───
struct DogFactory: AnimalFactory {
func createAnimal() -> Animal { Dog() }
}
struct CatFactory: AnimalFactory {
func createAnimal() -> Animal { Cat() }
}
3、外界,只需根据条件,要什么产品,就传入对应工厂(内部创建对应产品)
// ─── 客户端 ───
enum Scense {
case cat,
case dog
}
func makeAnimalSpeak(factory: AnimalFactory) {
let animal = factory.createAnimal()
print("\(animal.name) 说:\(animal.speak())")
}
func make(for scense: Scense){
switch scense:{
case cat:
makeAnimalSpeak(factory: DogFactory()) // 小狗 说:汪汪
case dog:
makeAnimalSpeak(factory: CatFactory()) // 小猫 说:喵喵
}
}
进阶:抽象工厂模式
思路:
在工厂模式基础上再抽象一层。普通工厂只生产一种产品,抽象工厂负责生产一族相关产品,保证同一工厂出来的产品风格一致,且调用方完全不依赖具体类。
核心区别一句话:工厂方法解决"造哪个",抽象工厂解决"造哪套"。
角色
-
多个产品协议:定义每种产品的功能、特点
-
遵守协议的类/struct:具体产品
-
工厂协议:定义每种产品的生产方法
-
遵守协议的类/struct:生产自己产品的 工厂
示例
抽象产品协议
// 产品族 协议
protocol Button {
func render()
func onTap()
}
protocol TextField {
func render()
func onInput(_ text: String)
}
// MARK: - iOS 具体产品
struct IOSButton: Button {
func render() { print(" 🔵 渲染 iOS 圆角按钮") }
func onTap() { print(" 📳 iOS Haptic 触感反馈") }
}
struct IOSTextField: TextField {
func render() { print(" 🔵 渲染 iOS 下划线输入框") }
func onInput(_ text: String) { print(" iOS 输入: \(text)") }
}
// MARK: - Android 具体产品
struct AndroidButton: Button {
func render() { print(" 🟢 渲染 Material Design 按钮") }
func onTap() { print(" 💧 Android Ripple 水波动效") }
}
struct AndroidTextField: TextField {
func render() { print(" 🟢 渲染 Material 边框输入框") }
func onInput(_ text: String) { print(" Android 输入: \(text)") }
}
抽象工厂协议
protocol UIFactory {
func makeButton() -> Button
func makeTextField() -> TextField
}
// 具体工厂(每个工厂只生产 自己的产品族)
struct IOSFactory: UIFactory {
func makeButton() -> Button { IOSButton() }
func makeTextField() -> TextField { IOSTextField() }
}
struct AndroidFactory: UIFactory {
func makeButton() -> Button { AndroidButton() }
func makeTextField() -> TextField { AndroidTextField() }
}
调用,客户端(只依赖抽象,不知道任何具体类型)
class LoginVC {
private let button: Button
private let textField: TextField
// 注入工厂,由外部决定平台风格
init(factory: UIFactory) {
self.button = factory.makeButton()
self.textField = factory.makeTextField()
}
func showUI() {
// 渲染登录界面
textField.render()
textField.onInput("alice@example.com")
button.render()
button.onTap()
}
}
// MARK: - 运行
LoginVC(factory: IOSFactory()).showUI()
LoginVC(factory: AndroidFactory()).showUI()
- 扩展
只需 新增 品类 和 工厂。不改 旧代码,不改调用方式(开闭原则),只需传递 新变量
// 新增 macOS 产品
struct MacOSButton: Button {
func render() { print(" 🖥 渲染 macOS NSButton") }
func onTap() { print(" 🖱 macOS 点击事件") }
}
struct MacOSTextField: TextField {
func render() { print(" 🖥 渲染 macOS NSTextField") }
func onInput(_ text: String) { print(" macOS 输入: \(text)") }
}
// 新增工厂
struct MacOSFactory: UIFactory {
func makeButton() -> Button { MacOSButton() }
func makeTextField() -> TextField { MacOSTextField() }
}
// 使用 —— LoginScreen 代码零修改
LoginScreen(factory: MacOSFactory()).show()
适用场景总结:
当系统需要独立于产品创建方式、且需要保证同一产品族的一致性时——比如跨平台 UI 套件、换肤主题、多数据库驱动(SQLite / CoreData / CloudKit)——抽象工厂是最合适的选择。
对比
实战场景
1、工厂模式 - 网络请求封装:根据环境(Mock / Real)切换网络层
// 实战:根据环境(Mock / Real)切换网络层
protocol NetworkSession {
func dataTask(with url: URL) async throws -> Data
}
struct LiveSession: NetworkSession {
func dataTask(with url: URL) async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
}
struct MockSession: NetworkSession {
func dataTask(with url: URL) async throws -> Data {
// 返回本地 JSON,不真正发网络请求
return """
{"id": 1, "name": "Mock User"}
""".data(using: .utf8)!
}
}
// 工厂方法
protocol SessionFactory {
func makeSession() -> NetworkSession
}
struct LiveSessionFactory: SessionFactory {
func makeSession() -> NetworkSession { LiveSession() }
}
struct MockSessionFactory: SessionFactory {
func makeSession() -> NetworkSession { MockSession() }
}
class UserRepository {
private let session: NetworkSession
init(factory: SessionFactory) {
self.session = factory.makeSession()
}
// ...
}
// 单元测试注入 Mock,生产注入 Live
let repo = UserRepository(factory: MockSessionFactory())
2、抽象工厂 - 封装 多主题 UI 系统(Dark / Light)
// 实战:一键切换整套 App 主题
protocol ThemeColor {
var primary: String { get }
var background: String { get }
var text: String { get }
}
protocol ThemeFont {
var titleSize: CGFloat { get }
var bodySize: CGFloat { get }
}
// 主题产品族 A:Light
struct LightColor: ThemeColor {
var primary = "#007AFF"
var background = "#FFFFFF"
var text = "#000000"
}
struct LightFont: ThemeFont {
var titleSize: CGFloat = 20
var bodySize: CGFloat = 16
}
// 主题产品族 B:Dark
struct DarkColor: ThemeColor {
var primary = "#0A84FF"
var background = "#1C1C1E"
var text = "#FFFFFF"
}
struct DarkFont: ThemeFont {
var titleSize: CGFloat = 22 // 暗色模式字体稍大
var bodySize: CGFloat = 17
}
// 抽象工厂
protocol ThemeFactory {
func makeColor() -> ThemeColor
func makeFont() -> ThemeFont
}
struct LightThemeFactory: ThemeFactory {
func makeColor() -> ThemeColor { LightColor() }
func makeFont() -> ThemeFont { LightFont() }
}
struct DarkThemeFactory: ThemeFactory {
func makeColor() -> ThemeColor { DarkColor() }
func makeFont() -> ThemeFont { DarkFont() }
}
// App 全局主题管理
class ThemeManager {
static var shared = ThemeManager(factory: LightThemeFactory())
let color: ThemeColor
let font: ThemeFont
init(factory: ThemeFactory) {
self.color = factory.makeColor()
self.font = factory.makeFont()
}
}
// 切换到深色主题,所有 color/font 同步更新
ThemeManager.shared = ThemeManager(factory: DarkThemeFactory())
总结
实际项目中两者常常配合出现:抽象工厂内部的每个 createXxx() 方法,本身就是一个工厂方法。抽象工厂是"工厂的工厂",是工厂方法向多产品维度的自然扩展。