“ 對象創建” 模式
通過“對象創建” 模式繞開new,來避免對象創建(new)過程中所導致的緊耦合(依賴具體類),從而支持對象創建的穩定。它是接口抽象之後的第一步工作。
典型模式
•Factory Method
•Abstract Factory
•Prototype
•Builder
Factory Method工廠方法
01 動機(Motivation)
在軟件系統中,經常面臨着創建對象的工作;由於需求的變化,需要創建的對象的具體類型經常變化。
如何應對這種變化?如何繞過常規的對象創建方法(new),提供一種“封裝機制”來避免客戶程序和這種“具體對象創建工作”的緊耦合?
02 模式定義
定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲(目的:解耦,手段:虛函數)到子類。
——《設計模式》GoF
03 結構
如上:依然紅色圈起來的代表穩定,藍色圈起來的代表變化。
04 樣例
這個僞代碼是實現對二進制文件,Txt,Prcture,Video等文件的切割
僞代碼 01
FileSplitter1.cpp
class ISplitter{
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
class VideoSplitter: public ISplitter{
};
MainForm1.cpp
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
ISplitter * splitter=
new BinarySplitter();//依賴具體類
//等號new左右兩邊都應該是依賴抽象類,而不是具體。
//不能用new不能像往常一樣創建一個對象,那看看有沒有其他的?
splitter->split();
}
};
先看一下上述代碼,每次我們如果需要對其他文件進行分割就得每次改void Button1_Click(),若對在除二進制文件,Txt,Prcture,Video等文件之外的文件切割,那麼我們不僅需要改源碼FileSplitter1.cpp還要改void Button1_Click()。這樣其實就是違反了依賴倒置的原則,在上述void Button1_Click()中說的很清楚,等號new左右兩邊都應該是依賴抽象類,而不是具體。
面向接口的編程告訴我們一個對象的類型往往聲明爲一個抽象類或接口,而不應該聲明爲具體類。否則意味着不能支持未來的變化。再來看加入工廠模式之後的僞代碼02
僞代碼02
FileSplitter.cpp
//抽象類
class ISplitter{
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
//工廠基類
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=0;
virtual ~SplitterFactory(){}
};
FileSplitter2.cpp
//具體類
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
class VideoSplitter: public ISplitter{
};
//虛函數是一個編譯時依賴
//具體工廠
class BinarySplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new BinarySplitter();
}
};
class TxtSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new TxtSplitter();
}
};
class PictureSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new PictureSplitter();
}
};
class VideoSplitterFactory: public SplitterFactory{
public:
virtual ISplitter* CreateSplitter(){
return new VideoSplitter();
}
};
MainForm2.cpp
class MainForm : public Form
{
SplitterFactory* factory;//工廠
public:
MainForm(SplitterFactory* factory){
this->factory=factory;
}
void Button1_Click(){
ISplitter * splitter=
factory->CreateSplitter(); //多態new
splitter->split();
}
};
對於上面代碼:首先我們把運行時依賴變成了編譯時依賴。不管文件種類是否變化我們的源文件MainForm2.cpp,FileSplitter.cpp都不用改變,若要添加種類,那我們就在FileSplitter2.cpp文件中進行擴展就Ok。
注:我們的做法並不是:並不是把依賴的具體類給消滅了,只是把具體類變化改到了一個局部地方
05 要點總結
Factory Method模式用於隔離類對象的使用者和具體類型之間的耦合關係。面對一個經常變化的具體類型,緊耦合關係(new)會導致軟件的脆弱。
Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關係。
Factory Method模式解決“單個對象”的需求變化。缺點在於要求創建方法/參數相同。