1. 程式人生 > 其它 >設計模式詳解:Factory Method(工廠方法)

設計模式詳解:Factory Method(工廠方法)

技術標籤:設計模式設計模式c++運維

Factory Method 工廠方法

設計模式學習:概述

意圖

定義一個用於建立物件的介面,讓子類決定例項化哪個類。Factory Method使得一個類的例項化延遲到其子類。

Factory Method又稱作Virtual Constructor(虛構造器)。

在軟體系統中,經常面臨著建立物件的工作,而需求的變化常常導致所需要建立的物件的具體型別發生變化。

Factory Method使得可以繞過常規的物件建立方法(new關鍵字),將封裝機制引入到物件的建立工作中,從而避免了客戶程式與“具體物件建立工作”產生緊耦合。

不過要注意,不可能做到完全繞過new進行物件建立,我們想要做的,是將建立物件的職責向上轉移,從而是子程式具有更高的靈活性。

程式碼案例

假如,現在有若干類,每一個子類代表一種動物:

class Animal{
    virtual void shout() = 0;
}

class Rabbit: public Anlmal{
    void shout(){cout << "ZI";}
}
class Cat: public Anlmal{
    void shout(){cout << "MIAO";}
}
class Dog: public Anlmal{
    void shout(){cout << "WANG"
;} }

我們在函式中這樣呼叫它:

void func()
{
    Animal* an = new Rabbit();
    an->shout();
}

如上所示,我們建立了一個兔子物件,並呼叫了它的Shout方法。

乍一看,其實這段程式碼並沒有什麼不妥的地方,但它實際上違反了一個重要的設計原則:依賴倒置原則,詳細來說,就是**基類物件的建立依賴於子類的存在。**也就是說,在這段程式碼中,class Rabbit必須存在來使得程式碼順利編譯通過。

聽起來不是什麼大事?舉個例子,在團隊合作的專案中,這就要求編寫函式的人必須擁有一個子類(大概率不是他本人負責編寫的類)的副本,擴大來說,團隊中的每一個人都必須擁有這個基類每個子類的副本

。在子類被更新時,每個人都必須更新自己手上的類檔案。

或許如果你學過編譯原理的話,用靜態連結和動態共享的類比會更好理解:這種方法白白佔用了更多的記憶體。更重要的是,它為專案的維護帶來了不便。下面我會對這一點做更詳細的解釋。

建立類設計模式的產生,就是為了將物件的建立作為一個單獨的模組維護,從而降低系統維護難度而產生,現在,我們來看看FactoryMethod怎樣解決這個問題。

解決方案:

首先建立這樣一個類:

class AnimalFactory
{
	virtual Animal* CreateAnimal() = 0;
}

接著,為每一個存在的Animal子類編寫對應的工廠子類。

class RabbitFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Rabbit();
	};
}

class CatFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Cat();
	};
}

class DogFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Dog();
	};
}

這樣,我們就可以用工廠來代替New進行物件建立了。為了滿足依賴倒置原則的要求,我們用這種方式來指定工廠的種類:

class solution
{
    AnimalFactory* af;
    solution(AnimalFactory* p): af(p){ }
    void func()
    {
        Animal* an = af.CreateAnimal();
        an.shout();
    }
}

這樣一來,solution類與Animal某一子類的緊耦合關係就被解除了,只需要建立solution物件時指定它對應的工廠即可。沒錯,這又是我們在設計模式學習中常見的技巧:編譯時多型轉執行時多型

看上去,我們只是把物件建立延遲到了上一級,有點多此一舉的意味。實際上,這對程式的封裝性是很大的改善。編寫solution類的程式猿,完全不需要知道子類是什麼,團隊又可以愉快地一起工作了。

解釋

事實上,建立物件型設計模式的原理都很類似(它們包括FactoryMethod、AbstractFactory, 而Builder不同點多一些)。都是將物件建立的過程作為一個單獨模組封裝起來,從而降低程式內部的耦合性。

FactoryMethod主要遵循了依賴倒置原則,它使得編寫基類相關的方法時不需要依賴特定的某個子類。

關於這一點,我在這裡用這樣一個例子來說明:你在考駕照時,不關心將來自己具體要開哪種車,只關心開的是手動擋、自動擋、小轎車還是大貨這種問題。不使用FactoryMethod的話,就好像你在學駕照的時候,就要清楚將來要開桑塔納還是別克,這就是一種依賴倒置。

再以上文為例:既然func函式與子類是什麼沒有關係,那麼就不應當要求存在某一特定的子類使得func函式編譯通過,甚至不存在任何一種子類時,它也應當正常編譯(相當於紙上談兵)

總結

設計模式Factory Method (工廠方法)
穩定點:工廠產品的引數(每種子類建立時需要的引數型別必須相同)
變化點:工廠的產品種類(子類型別)
效果:編寫操作基類的函式時不需要對其具體子類有任何的瞭解
特點:將物件建立作為單獨的模組封裝起來

類圖:

類圖:來自GOF《設計模式》
2021.2.9 轉載請標明出處