工廠模式(Factory Pattern)屬于創(chuàng)建型模式(Creational Pattern),F(xiàn)actory pattern 提供了一種在不暴露創(chuàng)建邏輯的情況下創(chuàng)建對象的方法。包含以下兩部分:

- Factory:創(chuàng)建對象。
- Products:被創(chuàng)建的對象。
從技術(shù)上講,工廠模式分為簡單工廠(Simple Factory)、抽象工廠(Abstract Factory)和其他。幾種模式的共同目標(biāo)是:將創(chuàng)建對象的邏輯封裝到自身構(gòu)造中。
1. 何時使用 factory pattern
當(dāng)想要分離產(chǎn)品創(chuàng)建邏輯,而非讓消費者(consumer)直接創(chuàng)建 product 時,使用工廠模式。
當(dāng)您擁有一組相關(guān)對象(例如多態(tài)子類,或?qū)崿F(xiàn)相同協(xié)議的多個對象)時,factory pattern 非常有用。例如,可以使用 factory pattern 檢查網(wǎng)絡(luò)響應(yīng),并將其轉(zhuǎn)換為具體的 model。
當(dāng)只有一種產(chǎn)品類型,且需要提供依賴或額外信息才能創(chuàng)建時,工廠模式也非常有用。例如,可以使用 factory pattern 創(chuàng)建一個自動回復(fù)求職者的郵件系統(tǒng),factory pattern 根據(jù)候選人被錄取、拒絕、邀請面試等狀態(tài)來生成具體的郵件信息。
2. Simple Factory
這一部分將會使用 simple factory 創(chuàng)建一個回復(fù)求職者的郵件系統(tǒng)。
先聲明JobApplicant和Emailmodel,代碼如下:
import Foundation
public struct JobApplicant {
public let name: String
public let email: String
public var status: Status
public enum Status {
case new
case interview
case hired
case rejected
}
}
public struct Email {
public let subject: String
public let messageBody: String
public let recipientEmail: String
public let senderEmail: String
}
每位求職者有name、email和四種不同的求職狀態(tài)。Email的subject和messageBody將根據(jù)求職狀態(tài)而變。
添加以下工廠方法:
import Foundation
public struct EmailFactory {
public let senderEmail: String
public func createEmail(to recipient: JobApplicant) -> Email {
let subject: String
let messageBody: String
switch recipient.status {
case .new:
subject = "We Received Your Application"
messageBody = "Thanks for applying for a job here!" + "You should hear from us in 1-3 business days."
case .interview:
subject = "We Want to Interview You"
messageBody = "Thanks for your resume,\(recipient.name)!" + "Can you come in for an interview in 30 minutes?"
case .hired:
subject = "We Want to Hire You"
messageBody = "Congratulations, \(recipient.name)!" + "We liked your code, and you smelled nice." + "We want to offer you a position! Cha-ching! $$$"
case .rejected:
subject = "Thanks for Your Application"
messageBody = "Thank you for applying,\(recipient.name)!" + "We have decided to move forward with other candidates." + "Please remeber to wear pants next time!"
}
return Email(subject: subject,
messageBody: messageBody,
recipientEmail: recipient.email,
senderEmail: senderEmail)
}
}
在上面的代碼中,先創(chuàng)建EmailFactory struct,并聲明一個 public 屬性senderEmail。在EmailFactory的初始化中會設(shè)置該屬性。createEmail(to recipient:)函數(shù)接受一個JobApplicant實參,返回Email,在createEmail(to recipient:)函數(shù)內(nèi),添加 switch 枚舉status,根據(jù)status不同生成不同subject和messageBody。
現(xiàn)在,郵件模版系統(tǒng)已經(jīng)構(gòu)建完成。是時候?qū)⒃?factory pattern 用于未來的申請人了。添加以下代碼:
var pro648 = JobApplicant(name: "pro648",
email: "pro648@example.com",
status: .new)
let emailFactory = EmailFactory(senderEmail: "about@example.com")
print(emailFactory.createEmail(to: pro648), "\n")
pro648.status = .interview
print(emailFactory.createEmail(to: pro648), "\n")
pro648.status = .hired
print(emailFactory.createEmail(to: pro648), "\n")
這里創(chuàng)建了JobApplicant和EmailFactory,使用emailFactory實例生成 email。
3. Abstract Factory
這一部分將介紹 abstract factory。與簡單工廠相比,抽象工廠處理更為復(fù)雜的情況。
假設(shè)我們要創(chuàng)建 Android 和 iOS 兩種類型的按鈕,我們先定義一個AbstractGUIFactory類,再定義具體的工廠類創(chuàng)建 Android 和 iOS 兩種類型的按鈕,具體的工廠類實現(xiàn)創(chuàng)建方法。如果需要創(chuàng)建其他類型控件(例如 alert),只需要在具體工廠類中添加創(chuàng)建方法即可。
聲明Button協(xié)議:
protocol Button {
func setTitle(_ title: String) -> Void
func show() -> Void
}
定義AndroidButton和iOSButton兩個類,該類遵守Button協(xié)議。
class AndroidButton: Button {
private var title: String?
func setTitle(_ title: String) {
self.title = title
}
func show() {
print("Showing Android style button. Title: \(title ?? "Default Title")")
}
}
class iOSButton: Button {
private var title: String?
func setTitle(_ title: String) {
self.title = title
}
func show() {
print("Showing iOS style button. Title: \(title ?? "Default Title")")
}
}
創(chuàng)建抽象工廠類AbstractGUIFactory,代碼如下:
protocol AbstractGUIFactory {
func createButton() -> Button
}
創(chuàng)建AndroidFactory和iOSFactory具體工廠類,如下所示:
class AndroidFactory: AbstractGUIFactory {
func createButton() -> Button {
return AndroidButton()
}
}
class iOSFactory: AbstractGUIFactory {
func createButton() -> Button {
return iOSButton()
}
}
最后,再創(chuàng)建一個GUIBuilder負(fù)責(zé)創(chuàng)建具體控件:
class GUIBuilder {
private var style: Style
private var guiFactory: AbstractGUIFactory?
public enum Style {
case iOS
case Android
}
init(style: Style) {
self.style = style
}
func initGUIFactory() -> Void {
if nil != guiFactory {
return
}
switch style {
case .iOS:
guiFactory = iOSFactory()
case .Android:
guiFactory = AndroidFactory()
}
}
func buildButton() -> Button {
initGUIFactory()
return guiFactory!.createButton()
}
}
下面,使用該工廠方法創(chuàng)建控件:
let androidBuilder = GUIBuilder(style: .Android)
let androidButton = androidBuilder.buildButton()
androidButton.setTitle("Be together, Not the same.")
androidButton.show()
let iOSBuilder = GUIBuilder(style: .iOS)
let iOSButton = iOSBuilder.buildButton()
iOSButton.setTitle("Power is power.")
iOSButton.show()
運行后輸出如下:
Showing Android style button. Title: Be together, Not the same.
Showing iOS style button. Title: Power is power.
當(dāng)我們想要創(chuàng)建其他控件時,只需要添加控件,在具體工廠方法中增加創(chuàng)建方法即可。例如,想要增加AndroidAlert和iOSAlert兩種類型控件,只需聲明兩種類型控件,在具體工廠中添加創(chuàng)建方法即可。
protocol Alert {
func setTitle(_ title: String) -> Void
func show() -> Void
}
// AndroidAlert和iOSAlert均遵守Alert協(xié)議。
class AndroidAlert: Alert {
private var title: String?
func setTitle(_ title: String) {
self.title = title
}
func show() {
print("Showing Android style Alert. Title: \(title ?? "Default Title")")
}
}
class iOSAlert: Alert {
private var title: String?
func setTitle(_ title: String) {
self.title = title
}
func show() {
print("Showing iOS style alert. Title: \(title ?? "Default Title")")
}
}
class AndroidFactory: AbstractGUIFactory {
...
// 增加創(chuàng)建 alert 方法
func createAlert() -> Alert {
return AndroidAlert()
}
}
class iOSFactory: AbstractGUIFactory {
...
// 增加創(chuàng)建 alert
func createAlert() -> Alert {
return iOSAlert()
}
}
class GUIBuilder {
...
// 增加創(chuàng)建alert
func buildAlert() -> Alert {
initGUIFactory()
return guiFactory!.createAlert()
}
}
// 具體應(yīng)用
let androidAlert = androidBuilder.buildAlert()
androidAlert.setTitle("github.com/pro648")
androidAlert.show()
let iOSAlert = iOSBuilder.buildAlert()
iOSAlert.setTitle("Knowledge is power.")
iOSAlert.show()
并不是所有多態(tài)對象都需要 factory pattern。如果對象非常簡單,則始終可以將創(chuàng)建邏輯放到 consumer 中(例如,視圖控制器)。
如果對象需要連續(xù)步驟來創(chuàng)建,生成器模式(Builder Patter)或許更為合適。
總結(jié)
以下是 factory design pattern 的關(guān)鍵點:
- Factory 的目標(biāo)是將創(chuàng)建對象的邏輯隔離到自身構(gòu)造中。
- 如果您擁有一組相關(guān) product,或在提供更多信息前(例如,接收到 response,或用戶輸入內(nèi)容)無法創(chuàng)建對象,則工廠模式非常有用。
- Factory design pattern 添加了一層抽象來創(chuàng)建對象,能夠減少重復(fù)代碼。
通過 factory pattern 可以再次減少視圖控制器代碼,遵守 Open/closed principle,降低耦合性。
工廠模式 Factory Pattern 和 策略模式 Strategy Pattern 有些相似,區(qū)別如下:
- Factory Pattern:是 creational pattern,用于創(chuàng)建特定類型對象。例如,創(chuàng)建狗、貓、老虎等不同類型動物。
- Strategy Pattern:是 behavioral pattern,以特定方式執(zhí)行操作。例如,執(zhí)行走、跑,跳等動作。
工廠模式和策略模式可以組合使用。例如,有一個創(chuàng)建 business 對象的工廠模式,其根據(jù)持久化策略不同選擇不同的工廠模式。如果數(shù)據(jù)保存到本地 XML ,使用 A 策略;如果數(shù)據(jù)保存到遠(yuǎn)程數(shù)據(jù)庫,使用 B 策略。
最為重要的是了解使用設(shè)計模式的動機,否則就像在木工店里用錘子切割木材。也就是說,在不適當(dāng)?shù)纳舷挛闹惺褂迷O(shè)計模式就是在反設(shè)計模式,因此請確保了解設(shè)計模式的動機。
SimpleFactory 源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/SimpleFactory
AbstractFactory 源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/AbstractFactory
參考資料: