設計模式-工廠方法模式

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

工廠方法模式的思考

工廠方法模式的缺點

  • 在添加新產品時,需要編寫新的具體產品類,而且還需要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的復雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷
  • 由于考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度。
posted @ 2019-10-31 17:58  Rimond_Jing  閱讀(...)  評論(... 編輯 收藏
11选5走势图