設計模式-工廠方法模式

  • 2019 年 10 月 31 日
  • 筆記

20191030175212.jpg

概念

工廠方法模式(Factory Method Pattern)又稱工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多態工廠(Polymorphic Factory)模式,在工廠方法模式中,工廠父類負責定義創建產品對象的公共介面,而工廠子類則負責生成具體的產品對象,這樣做的目的是將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。
工廠方法,可以理解為一種將實例化邏輯委託給子類的方法,父基類提供共有實現,派生類完成個性化訂製實現

使用時機

如果有這樣的場景,存在部分共有的功能邏輯,但是在運行時又需要動態決定使用哪個子類的邏輯實現,也就是說
需要動態根據需求選擇不同的子類實現,這個時候可以使用工廠方法。
建議在以下情況下選用工廠方法模式。

  • 如果一個類需要創建某個介面的對象,但是又不知道具體的實現,這種情況可以選用工廠方法模式,把創建對象的工作延遲到子類中去實現。
  • 如果一個類本身就希望由他的子類來創建所需的對象的時候,應該使用工廠方法模式。

與簡單工廠的區別

初接觸工廠方法模式,會感覺,「這不是跟簡單工廠一樣嗎」

從本質上講,他們確實是非常類似的,在具體實現上都是「選擇實現」。但是也存在不同點,簡單工廠是直接在工廠類里進行「選擇實現」;而工廠方法會把這個工作延遲到子類來實現,工廠類裡面使用工廠方法的地方是依賴於抽象而不是具體的實現,從而使得系統更加靈活,具有更好的可維護性和可擴展性。

從某個角度講,可以認為簡單工廠就是工廠方法模式的一種特例,因此他們的本質是類似的。

具體的舉例,簡單工廠實例中,客戶就是要一個門,而不關心創建過程,最後實際創造的是一個木門,這個比較尷尬,如果客戶要的是個鐵門呢?所以在這個例子中,也是存在兩維抽象的,一是「門」這個類型的抽象,二是「造門」這個方法的抽象。簡單工廠只做到了前者,而沒有給出後者的解決方案,如果看我們按照工廠方法的思路,將門工廠造門這件事進行細分,木門交給木門工廠,鐵門交給鐵門工廠,這就是工廠方法的實現,客戶需要先制定委託對象,而不必關係具體怎麼造門。

具體實現

C++實現

結構圖

uml1.png

程式碼實現

#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實現

結構圖

factory-method.png

程式碼實現

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")  ......

工廠方法模式的思考

工廠方法模式的缺點

  • 在添加新產品時,需要編寫新的具體產品類,而且還需要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷
  • 由於考慮到系統的可擴展性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度。