設計模式-工廠方法模式
- 2019 年 10 月 31 日
- 筆記
概念
工廠方法模式(Factory Method Pattern)又稱工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多態工廠(Polymorphic Factory)模式,在工廠方法模式中,工廠父類負責定義創建產品對象的公共介面,而工廠子類則負責生成具體的產品對象,這樣做的目的是將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。
工廠方法,可以理解為一種將實例化邏輯委託給子類的方法,父基類提供共有實現,派生類完成個性化訂製實現
使用時機
如果有這樣的場景,存在部分共有的功能邏輯,但是在運行時又需要動態決定使用哪個子類的邏輯實現,也就是說
需要動態根據需求選擇不同的子類實現,這個時候可以使用工廠方法。
建議在以下情況下選用工廠方法模式。
- 如果一個類需要創建某個介面的對象,但是又不知道具體的實現,這種情況可以選用工廠方法模式,把創建對象的工作延遲到子類中去實現。
- 如果一個類本身就希望由他的子類來創建所需的對象的時候,應該使用工廠方法模式。
與簡單工廠的區別
初接觸工廠方法模式,會感覺,「這不是跟簡單工廠一樣嗎」
從本質上講,他們確實是非常類似的,在具體實現上都是「選擇實現」。但是也存在不同點,簡單工廠是直接在工廠類里進行「選擇實現」;而工廠方法會把這個工作延遲到子類來實現,工廠類裡面使用工廠方法的地方是依賴於抽象而不是具體的實現,從而使得系統更加靈活,具有更好的可維護性和可擴展性。
從某個角度講,可以認為簡單工廠就是工廠方法模式的一種特例,因此他們的本質是類似的。
具體的舉例,簡單工廠實例中,客戶就是要一個門,而不關心創建過程,最後實際創造的是一個木門,這個比較尷尬,如果客戶要的是個鐵門呢?所以在這個例子中,也是存在兩維抽象的,一是「門」這個類型的抽象,二是「造門」這個方法的抽象。簡單工廠只做到了前者,而沒有給出後者的解決方案,如果看我們按照工廠方法的思路,將門工廠造門這件事進行細分,木門交給木門工廠,鐵門交給鐵門工廠,這就是工廠方法的實現,客戶需要先制定委託對象,而不必關係具體怎麼造門。
具體實現
C++實現
結構圖
程式碼實現
#include <iostream> class IInterviewer { public: virtual void askQuestions() = 0; }; class Developer : public IInterviewer { public: void askQuestions() override { std::cout << "Asking about design patterns!" << std::endl; } }; class CommunityExecutive : public IInterviewer { public: void askQuestions() override { std::cout << "Asking about community building!" << std::endl; } }; class HiringManager { public: void takeInterview() { IInterviewer* interviewer = this->makeInterviewer(); interviewer->askQuestions(); } protected: virtual IInterviewer* makeInterviewer() = 0; }; template <typename Interviewer> class OtherManager : public HiringManager { protected: IInterviewer* makeInterviewer() override { return new Interviewer(); } }; int main() { HiringManager* devManager = new OtherManager<Developer>(); devManager->takeInterview(); HiringManager* marketingMnager = new OtherManager<CommunityExecutive>(); marketingMnager->takeInterview(); return 0; }
運行結果
g++ -o factory-method factory-method.cpp --std=c++11 ./factory-method Asking about design patterns! Asking about community building!
總結
IInterviwer為基礎類,定義為一個虛基類,具體派生出了Devoloper和CommunityExecutive兩個實現子類;
HiringManager為工廠基類,定義為一個虛基類,OtherManager為具體實現的派生類,根據傳入的template type,返回不同的創建對象。
Golang實現
結構圖
程式碼實現
package factory_method //Operator是被封裝的介面 type Operator interface { SetA(int) SetB(int) Result() int } //工廠介面 type OperatorFactory interface { Create() Operator } //OperatorBase是Operator介面實現的基類,封裝公有方法 type OperatorBase struct { a,b int } //SetA方法實現 func (op *OperatorBase) SetA(aValue int) { op.a = aValue } //SetB方法實現 func (op *OperatorBase) SetB(bValue int) { op.b = bValue } //下面是分別構造不同類型Operator以及對應生產的工廠類 //加法工廠類 type PlusOperatorFactory struct { } //加法工廠類實現工廠介面 func (pof PlusOperatorFactory) Create() Operator { return &PlusOperator{} } //加法類 type PlusOperator struct { OperatorBase } //加法類實現Operator介面的Result函數 func (pop *PlusOperator) Result() int { return pop.a + pop.b } //減法工廠類 type MinusOperatorFactory struct { } //減法工廠類實現工廠介面 func (mof MinusOperatorFactory) Create() Operator { return &MinusOperator{} } //減法類 type MinusOperator struct { OperatorBase } //減法類實現Operator介面的Result函數 func (mop *MinusOperator) Result() int { return mop.a - mop.b }
測試用例
package factory_method import "testing" func TestPlusOperator_Result(t *testing.T) { pof := PlusOperatorFactory{} pop := pof.Create() pop.SetA(1) pop.SetB(2) if pop.Result() != 3 { t.Fatal("test plus operator factory failed") } } func TestMinusOperator_Result(t *testing.T) { mof := MinusOperatorFactory{} mop := mof.Create() mop.SetA(2) mop.SetB(1) if mop.Result() != 1 { t.Fatal("test minus operator factory failed") } }
運行結果
go test -v factory-method.go factory-method_test.go === RUN TestPlusOperator_Result --- PASS: TestPlusOperator_Result (0.00s) === RUN TestMinusOperator_Result --- PASS: TestMinusOperator_Result (0.00s) PASS ok command-line-arguments 0.734s
優化後更實用的版本
func LoadFactory(name string) OperatorFactory { switch name { case "plus": return PlusOperatorFactory{} case "minus": return MinusOperatorFactory{} default: return PlusOperatorFactory{} } }
測試用例
...... pof := LoadFactory("plus") ......
工廠方法模式的思考
工廠方法模式的缺點
- 在添加新產品時,需要編寫新的具體產品類,而且還需要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷
- 由於考慮到系統的可擴展性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度。