wiki: 工廠方法模式(英語:Factory method pattern)是一種實現了「工廠」概念的面向對象設計模式。就像其餘建立型模式同樣,它也是處理在不指定對象具體類型的狀況下建立對象的問題。工廠方法模式的實質是「定義一個建立對象的接口,但讓實現這個接口的類來決定實例化哪一個類。工廠方法讓類的實例化推遲到子類中進行。」git
上面是 維基百科 中對工廠方法的定義,在上一篇 golang設計模式之簡單工廠模式 中咱們介紹過,惟一的一個工廠控制着 全部產品的實例化,而 工廠方法
中包括一個工廠接口,咱們能夠動態的實現多種工廠,達到擴展的目的github
簡單工廠須要:golang
工廠方法須要:web
在 簡單工廠
中,依賴於惟一的工廠對象,若是咱們須要實例化一個產品,那麼就要向工廠中傳入一個參數獲取對應對象,若是要增長一種產品,就要在工廠中修改建立產品的函數,耦合性太高 ,而在 工廠方法
中,依賴工廠接口,咱們能夠經過實現工廠接口,建立多種工廠,將對象建立由一個對象負責全部具體類的實例化,變成由一羣子類來負責對具體類的實例化,將過程解耦。設計模式
下面用代碼實現:bash
例如,咱們如今有一個產品須要被建立,那麼先構建工廠接口和產品接口框架
// 工廠接口
type FactoryInterface interface {
CreateProduct(t string) ProductInterface
}
// 產品接口
type ProductInterface interface {
Intro()
}
複製代碼
而後實現這兩個接口: 工廠結構體和產品結構體函數
// 建立工廠結構體並實現工廠接口
type Factory1 struct {
}
func (f Factory1) CreateProduct(t string) ProductInterface {
switch t {
case "product1":
return Product1{}
default:
return nil
}
}
// 建立產品1並實現產品接口
type Product1 struct {
}
func (p Product1) Intro() {
fmt.Println("this is product 1")
}
複製代碼
這樣在使用的時候,就可讓子類來選擇實例化哪一種產品:post
// 建立工廠
f := new(Factory1)
p := f.CreateProduct("product1")
p.Intro() // output: this is product 1.
複製代碼
或許上面的代碼看起來並不容易懂,由於咱們只有一種產品,不能看出來它的好處,在網上我看到了一個賣包子的例子,我以爲很貼切,在這我就用go實現一下,輔助理解:ui
栗子: 我如今想在個人老家齊齊哈爾開一家包子店,賣豬肉餡和三鮮餡兩種餡料的包子,那麼咱們使用簡單工廠模式應該怎樣實現呢?
簡單工廠模式實現:
建立工廠結構體(包子店)
// 工廠類(包子店)
type BunShop struct {
}
複製代碼
建立產品接口(包子類的接口)
type Bun interface {
create()
}
複製代碼
實現產品(2種包子)
type PigMeatBuns struct {}
func (p PigMeatBuns) create() {
fmt.Println("豬肉餡包子")
}
type SamSunStuffingBuns struct {}
func (s SamSunStuffingBuns) create() {
fmt.Println("三鮮餡包子")
}
複製代碼
func (b BunShop) Generate(t string) Bun {
switch t {
case "pig":
return PigMeatBuns{}
case "3s":
return SamSunStuffingBuns{}
default:
return nil
}
}
複製代碼
factory := new(BunShop)
b := factory.Generate("pig")
b.create() // output: 豬肉餡包子
複製代碼
但是若是生意作的不錯,我想要在廣東開一家分店該怎麼辦呢?依舊是兩種包子,可是爲了符合當地人的口味,必定會有所差異,難道要一步一步的修改工廠類嗎?
這樣工廠方法模式就派上用場了...
添加工廠接口(包子店的接口)和產品接口(包子接口)
type BunShopInterface interface{
Generate(t string) Bun
}
type Bun interface {
create()
}
複製代碼
建立工廠結構體和產品結構體(具體包子店和具體包子)
type QSPigMeatBuns struct{}
type GDPigMeatBuns struct{}
type QSSamSunStuffingBuns struct{}
type GDSamSunStuffingBuns struct{}
// 實現產品接口... 這裏就不寫了
// CODE ...
複製代碼
type QSBunShop struct {}
type GDBunShop struct {}
func (qs QSBunShop) Generate(t string) Bun {
switch t {
case "pig":
return QSPigMeatBuns{}
case "3s":
return QSSamSunStuffingBuns{}
default:
return nil
}
}
func (gd QSBunShop) Generate(t string) Bun {
switch t {
case "pig":
return GDPigMeatBuns{}
case "3s":
return GDSamSunStuffingBuns{}
default:
return nil
}
}
複製代碼
var b Bun
// 賣呀賣呀賣包子...
QSFactory := new(QSBunShop)
b = QSFactory.Generate("pig") // 傳入豬肉餡的參數,會返回齊市包子鋪的豬肉餡包子
b.create()
GDFactory := new(GDBunShop)
b = GDFactory.Generate("pig") // 一樣傳入豬肉餡的參數,會返回廣東包子鋪的豬肉餡包子
b.create()
複製代碼
go中沒有繼承,實際上能夠以組合的方式達到繼承的目的
簡單工廠模式和工廠方法模式看起來很類似,本質區別就在於,若是在包子店中直接建立包子產品,是依賴具體包子店的,擴展性、彈性、可維護性都較差,而若是將實例化的代碼抽象出來,再也不依賴具體包子店,而是依賴於抽象的包子接口,使對象的實現從使用中解耦,這樣就擁有很強的擴展性了,也能夠稱爲 『依賴倒置原則』
工廠方法模式的優缺點
優勢:
符合「開閉」原則,具備很強的的擴展性、彈性和可維護性。修改時只須要添加對應的工廠類便可
使用了依賴倒置原則,依賴抽象而不是具體,使用(客戶)和實現(具體類)鬆耦合
客戶只須要知道所需產品的具體工廠,而無須知道具體工廠的建立產品的過程,甚至不須要知道具體產品的類名。
缺點:
每增長一個產品時,都須要一個具體類和一個具體建立者,使得類的個數成倍增長,致使系統類數目過多,複雜性增長
對簡單工廠,增長功能修改的是工廠類;對工廠方法,增長功能修改的是產品類。
上述代碼均放在 golang-design-patterns 這個倉庫中
打個廣告,推薦一下本身寫的 go web框架 bingo,求star,求PR ~