設計模式實戰 - 抽象工廠模式(Abstract Factory Pattern)
0 導讀
工廠方法模式人是造出來了,可都是清一色的型別,缺少關愛、仇恨、喜怒哀樂等情緒,人類的生命太平淡了,忘記給人類定義性別了,那怎麼辦?
從頭開始建立所有的事物也是不可能的,那就想在現有的條件下重新造人,儘可能舊物利用嘛
人種(Product產品類)應該怎麼改造呢?怎麼才能讓人類有愛有恨呢?定義互斥的性別,然後在每個個體中埋下一顆種子:異性相吸,成熟後就一定會去找個異性
從設計角度來看,一個具體的物件通過兩個座標就可以確定:膚色和性別
- 產品類分析完,生產的工廠類(八卦爐)該怎麼改造呢?
只有一個生產裝置,要麼生產出來的全都是男性,要麼都是女性,何以解憂?
把目前已經有的生產裝置—八卦爐拆開,於是女媧就使用了“八卦複製術”,把原先的八卦爐一個變兩個,並且略加修改,就成了女性八卦爐(只生產女性人種)和男性八卦爐(只生產男性人種),於是就開始準備生產
一個介面,多個抽象類,然後是N個實現類,每個人種都是一個抽象類,性別是在各個實現類中實現的
特別需要說明的是HumanFactory
介面,在這個介面中定義了三個方法,分別用來生產三個不同膚色的人種,也就是我們在座標圖的Y座標,它的兩個實現類分別是性別,也就是座標圖的X座標
通過X座標(性別)和Y座標(膚色)唯一確定了一個生產出來的物件 -
Human介面如程式碼
-
人種有三個抽象類,負責人種的抽象屬性定義
膚色和語言,白色人種、黑色人種、黃色人種
每個抽象類都有兩個實現類,分別實現公共的最細節、最具體的事物:膚色和語言
具體的實現類實現膚色、性別定義
-
以黃色女性人種為例
-
黃色男性人種
剩下的工作就是怎麼製造人類
-
介面HumanFactory
在介面中,看到八卦爐是可以生產出不同膚色人種的,有多少個八卦爐呢?
兩個,分別生產女性和男性,女性和男性八卦爐
人種有了,八卦爐也有了,我們就來重現一下造人的光景
各種膚色的男性、女性都製造出來了
回頭來想想我們的設計,不知道大家有沒有去過工廠,每個工廠分很多車間,每個車間又分多條生產線,分別生產不同的產品
我們可以把八卦爐比喻為車間,把八卦爐生產的工藝(生產白人、黑人還是黃人)稱為生產線,如此來看就是一個女性生產車間,專門生產各種膚色的女性,一個是男性生產車間,專門生產各種膚色男性
在這樣的設計下,各個車間和各條生產線的職責非常明確,在車間內各個生產出來的產品可以有耦合關係,你要知道世界上黑、黃、白人種的比例是:1∶4∶6,那這就需要女媧娘娘在燒製的時候就要做好比例分配,在一個車間內協調好
這就是抽象工廠模式
1 定義
-
官方定義
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
抽象工廠模式的通用類圖
抽象工廠模式是工廠方法模式的升級版本
在有多個業務品種、業務分類時,通過抽象工廠模式產生需要的物件是一種非常好的解決方式
我們來看看抽象工廠的通用原始碼,首先有兩個互相影響的產品線(產品族),例如製造汽車的左側門和右側門,這兩個應該是數量相等的——兩個物件之間的約束,每個型號的車門都是不一樣的,這是產品等級結構約束的,我們先看看兩個產品族的類圖
抽象工廠模式的通用原始碼類圖
注意類圖上的圈圈、框框相對應,兩個抽象的產品類可以有關係,例如共同繼承或實現一個抽象類或介面 -
抽象產品類
-
產品A1的實現類
-
產品A2的實現類
產品B與此類似,不再贅述
-
抽象工廠類
抽象工廠類AbstractCreator
的職責是定義每個工廠要實現的功能,在通用程式碼中,抽象工廠類定義了兩個產品族的產品建立
有N個產品族,在抽象工廠類中就應該有N個建立方法
如何建立一個產品,則是由具體的實現類來完成的
-
產品等級1的實現類
-
產品等級2的實現類
有M個產品等級就應該有M個實現工廠類,在每個實現工廠中,實現不同產品族的生產任務
在具體的業務中如何產生一個與實現無關的物件呢?
在場景類中,沒有任何一個方法與實現類有關係,對於一個產品來說,我們只要知道它的工廠方法就可以直接產生一個產品物件,無須關心它的實現類
2 適用場景
一個物件族(或是一組沒有任何關係的物件)都有相同的約束
例如一個文字編輯器和一個圖片處理器,都是軟體實體,但是*nix下的文字編輯器和Windows下的文字編輯器雖然功能和介面都相同,但是程式碼實現是不同的,圖片處理器也有類似情況。也就是具有了共同的約束條件:作業系統型別。於是我們可以使用抽象工廠模式,產生不同作業系統下的編輯器和圖片處理器
3 優點
封裝性
每個產品的實現類不是高層模組要關心的
它要關心的是什麼?是介面,是抽象,它不關心物件是如何創建出來,這由誰負責呢?工廠類,只要知道工廠類是誰,我就能創建出一個需要的物件,省時省力,優秀設計就應該如此
產品族內的約束為非公開狀態
例如生產男女比例的問題上,肯定有自己的打算,不能讓女盛男衰,否則女性的優點不就體現不出來了嗎?
那在抽象工廠模式,就應該有這樣的一個約束:每生產1個女性,就同時生產出1.2個男性,這樣的生產過程對呼叫工廠類的高層模組來說是透明的,它不需要知道這個約束,我就是要一個黃色女性產品就可以了,具體的產品族內的約束是在工廠內實現的
4 缺點
產品族擴充套件非常困難
以通用程式碼為例,如果要增加一個產品C,也就是說產品家族由原來的2個增加到3個,看看我們的程式有多大改動吧!
抽象類AbstractCreator要增加一個方法createProductC(),然後兩個實現類都要修改,想想看,這嚴重違反了開閉原則,而且我們一直說明抽象類和介面是一個契約
改變契約,所有與契約有關係的程式碼都要修改,那麼這段程式碼叫什麼?叫“有毒程式碼”,——只要與這段程式碼有關係,就可能產生侵害的危險!
是產品族擴充套件困難,而不是產品等級
在該模式下,產品等級是非常容易擴充套件的,增加一個產品等級,只要增加一個工廠類負責新增加出來的產品生產任務即可。也就是說橫向擴充套件容易,縱向擴充套件困難。以人類為例子,產品等級中只有男、女兩個性別,現實世界還有一種性別:雙性人,那我們要擴充套件這個產品等級也是非常容易的,增加三個產品類,分別對應不同的膚色,然後再建立一個工廠類,專門負責不同膚色人的雙性人的建立任務,完全通過擴充套件來實現需求的變更,從這一點上看,抽象工廠模式是符合開閉原則的
產品等級結構與產品族
當一個工廠可以創建出分屬於不同產品等級結構的一個產品族中的所有物件時
抽象工廠比工廠方法更適合!!!
5 實踐 coding
Java 類相關
Python相關
工廠方法關注產品等級結構
抽象工廠關注產品族
6 原始碼應用
獲取的都是 MySQL 的 db 連線,為一個產品族
Mybatis工廠應用
首先從配置中獲取環境變數
初始化事務工廠,並對其賦值
再從事務工廠中獲取事務例項物件
再以之為引數得到執行執行緒池
最後再生成返回預設 sqlsession 例項
7 最佳實踐
抽象工廠模式是一個簡單的模式,使用的場景非常多,大家在軟體產品開發過程中,涉及不同作業系統的時候,都可以考慮使用抽象工廠模式,例如一個應用,需要在三個不同平臺上執行,你會怎麼設計?分別設計三套不同的應用?
非也,通過抽象工廠模式遮蔽掉作業系統對應用的影響。三個不同作業系統上的軟體功能、應用邏輯、UI都應該是非常類似的,唯一不同的是呼叫不同的工廠方法,由不同的產品類去處理與作業系統互動的資訊