基於go語言學習工廠模式

Rick.lz發表於2021-10-12

工廠模式

一般情況下,工廠模式分為三種更加細分的型別:簡單工廠、工廠方法和抽象工廠。不過,在GoF的《設計模式》一書中,它將簡單工廠模式看作是工廠方法模式的一種特例,所以工廠模式只被分成了工廠方法和抽象工廠兩類。

在這三種細分的工廠模式中,簡單工廠、工廠方法原理比較簡單,在實際的專案中也比較常用。我們這裡來重點的介紹下這兩種。

簡單工廠模式(Simple Factory)

定義

定義一個工廠類,它可以根據引數的不同返回不同類的例項,被建立的例項通常都具有共同的父類。

因為在簡單工廠模式用於建立例項的方法是靜態的方法,因此簡單工廠模式又被稱為靜態工廠方法模式,它屬於類建立型模式。

優點

  • 工廠類含有必要的判斷邏輯,可以決定在什麼時候建立哪一個產品類的例項,客戶端可以免除直接建立產品物件的責任,而僅僅“消費”產品;簡單工廠模式通過這種做法實現了對責任的分割,它提供了專門的工廠類用於建立物件。

  • 客戶端無須知道所建立的具體產品類的類名,只需要知道具體產品類所對應的引數即可,對於一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。

  • 通過引入配置檔案,可以在不修改任何客戶端程式碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。

缺點

  • 由於工廠類集中了所有產品建立邏輯,一旦不能正常工作,整個系統都要受到影響。

  • 使用簡單工廠模式將會增加系統中類的個數,在一定程式上增加了系統的複雜度和理解難度。

  • 系統擴充套件困難,一旦新增新產品就不得不修改工廠邏輯,在產品型別較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴充套件和維護。

  • 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。

適用範圍

工廠類負責建立的物件比較少,客戶只知道傳入了工廠類的引數,對於始何建立物件(邏輯)不關心。

程式碼實現

package main

import "fmt"

func main() {
	f := getFruit("apple")
	fmt.Println(f.Fruit())
}

type FruitFactory interface {
	Fruit() string
}

func getFruit(t string) FruitFactory {
	switch t {
	case "apple":
		return &apple{}
	case "banana":
		return &banana{}
	}

	return nil
}

type apple struct{}

func (*apple) Fruit() string {
	return "我是蘋果,我很好吃"
}

type banana struct{}

func (*banana) Fruit() string {
	return "我是香蕉,我最好吃了"
}

總結下:

主要是通過 if 來判斷邏輯,當我們有新的實現需要加入,只需要新增對應的 if 判斷就好了。

UML 類圖

factory

工廠方法模式(Factory Method)

定義

工廠方法模式(英語:Factory method pattern)是一種實現了“工廠”概念的物件導向設計模式。就像其他建立型模式一樣,它也是處理在不指定物件具體型別的情況下建立物件的問題。工廠方法模式的實質是“定義一個建立物件的介面,但讓實現這個介面的類來決定例項化哪個類。工廠方法讓類的例項化推遲到子類中進行。”

優點

  • 一個呼叫者想建立一個物件,只要知道其名稱就可以了。

  • 擴充套件性高,如果想增加一個產品,只要擴充套件一個工廠類就可以。

  • 遮蔽產品的具體實現,呼叫者只關心產品的介面。

缺點

每次增加一個產品時,都需要增加一個具體類和物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

適用範圍

當物件的建立邏輯比較複雜,不只是簡單的 new 一下就可以,而是要組合其他類物件,做各種初始化操作的時候,推薦使用工廠方法模式,將複雜的建立邏輯拆分到多個工廠類中,讓每個工廠類都不至於過於複雜。

程式碼實現

package main

import "fmt"

func main() {
	apple := appleFactory{}
	fmt.Println(apple.Fruit())

	banana := bananaFactory{}
	fmt.Println(banana.Fruit())
}

type Fruit interface {
	Fruit() string
}

type appleFactory struct{}

func (*appleFactory) Fruit() string {
	return "我是蘋果,我很好吃"
}

type bananaFactory struct{}

func (*bananaFactory) Fruit() string {
	return "我是香蕉,我最好吃了"
}

總結:

工廠方法模式實現的時,客戶端需要決定例項化哪一個工廠來決定選擇那種水果,還是需要判斷的,只不過這個判斷交給客戶端進行了。簡單工廠是修改工廠類,這裡只需要修改客戶端。

UML 類圖

factory

抽象工廠模式(Abstract Factory)

定義

抽象工廠模式(Abstract Factory),提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。

優點

抽象工廠模式除了具有工廠方法模式的優點外,最主要的優點就是可以在類的內部對產品族進行約束。所謂的產品族,一般或多或少的都存在一定的關聯,抽象工廠模式就可以在類內部對產品族的關聯關係進行定義和描述,而不必專門引入一個新的類來進行管理。

  • 易於交換產品系列,由於具體的工場類,在使用的時候只需要在應用中初始化一次,所以改變工廠就很簡單,只需要改變具體地工廠就能使用對應的配置資訊。

  • 它讓具體地建立過程和客戶端分離,客戶端通過他們的抽象介面操作例項,產品的具體類名也和具體工廠分離,不會出現在客戶端程式碼中。

缺點

抽象工廠模式在於難於應付“新物件”的需求變動。難以支援新種類的產品。難以擴充套件抽象工廠以生產新種類的產品。這是因為抽象工廠幾乎確定了可以被建立的產品集合,支援新種類的產品就需要擴充套件該工廠介面,這將涉及抽象工廠類及其所有子類的改變。

適用範圍

  • 1.一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有形態的工廠模式都是重要的。

  • 2.這個系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。

  • 3.同屬於同一個產品族的產品是在一起使用的,這一約束必須在系統的設計中體現出來。(比如:Intel主機板必須使用Intel CPU、Intel晶片組)

  • 4.系統提供一個產品類的庫,所有的產品以同樣的介面出現,從而使客戶端不依賴於實現。

程式碼實現

package main

import "fmt"

func main() {
	f := WuhanFruitFactory{}
	b := f.ChooseApple()
	b.Fruit()
}

type FruitInterface interface {
	ChooseApple() ProductInterface
	ChooseBanana() ProductInterface
}

type ProductInterface interface {
	Fruit()
}

type HainanApple struct {
}

func (h HainanApple) Fruit() {
	fmt.Println("我是蘋果,來自海南")
}

type HainanBanana struct {
}

func (h HainanBanana) Fruit() {
	fmt.Println("我是香蕉,來自海南")
}

type WuhanApple struct {
}

func (w WuhanApple) Fruit() {
	fmt.Println("我是蘋果,來自武漢")
}

type WuhanBanana struct {
}

func (w WuhanBanana) Fruit() {
	fmt.Println("我是香蕉,來自武漢")
}

type WuhanFruitFactory struct {
}

func (w WuhanFruitFactory) ChooseApple() ProductInterface {
	return WuhanApple{}
}

func (w WuhanFruitFactory) ChooseBanana() ProductInterface {
	return WuhanBanana{}
}

type HainanFruitFactory struct {
}

func (gd HainanFruitFactory) ChooseApple() ProductInterface {
	return HainanApple{}
}

func (gd HainanFruitFactory) ChooseBanana() ProductInterface {
	return HainanBanana{}
}

UML 類圖

factory

總結:

這裡抽象出了兩個工廠,然後每個工廠中實現自己的方法。

客戶端在使用的時候只需要在應用中初始化一次,所以改變工廠就很簡單,只需要改變具體地工廠就能使用對應的配置資訊。

針對抽象工廠模式,我們會宣告那個工廠,當然進一步擴充套件,我們可以結合依賴注入,上游將工廠注入進來,下游根據注入的工廠進行初始化,這樣就更加靈活了,依賴注入模式,我們後面在接著分析。

參考

【工廠方法模式】https://wiki.jikexueyuan.com/project/java-design-pattern/factory-pattern.html
【抽象工廠模式】https://refactoringguru.cn/design-patterns/abstract-factory
【極客時間】設計模式之美
【抽象工廠】https://www.liaoxuefeng.com/wiki/1252599548343744/1281319134822433
【簡單工廠模式,工廠方法模式和抽象工廠模式的異同】https://blog.csdn.net/gatieme/article/details/17525805
【大話設計模式】https://book.douban.com/subject/2334288/
【工廠模式】https://boilingfrog.github.io/2021/10/12/基於go學習工廠模式/

相關文章