本周內(nèi)容
(1)工廠方法
(2)抽象工廠
(3)原型模式
(4)構(gòu)建器
(5)門面模式
(6)代理模式
(7)適配器
(8)中介者
“對象創(chuàng)建”模式
通過“對象創(chuàng)建”模式繞開new,來避免對象創(chuàng)建(new)過程中所導(dǎo)致的緊耦合(依賴具體類)仇奶,從而支持對象創(chuàng)建的穩(wěn)定员帮。它是接口抽象之后的第一步工作妹笆。
-
典型模式
- Factory Method
- Abstract Factory
- Prototype
- Builder
一 工廠方法
動機(jī)
- 在軟件系統(tǒng)中吃嘿,經(jīng)常面臨著創(chuàng)建對象的工作硝逢;由于需求的變化的妖,需要創(chuàng)建的對象的具體類型經(jīng)常變化矢洲。
- 如何應(yīng)對這種變化璧眠?如何繞過常規(guī)的對象創(chuàng)建方法(new),提供一種“封裝機(jī)制”來避免客戶程序和這種“具體對象創(chuàng)建工作”的緊耦合读虏?
模式定義:
定義一個用于創(chuàng)建對象的接口责静,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲(目的:解耦盖桥,手段:虛函數(shù))到子類灾螃。——《設(shè)計模式》GoF
工廠模式改造前:
//MainForm1.cpp
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
ISplitter * splitter=
new BinarySplitter();//依賴具體類
splitter->split();
}
};
//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{
};
工廠模式改造后:
//MainForm2.cpp
class MainForm : public Form
{
SplitterFactory* factory;//工廠
public:
MainForm(SplitterFactory* factory){
this->factory=factory;
}
void Button1_Click(){
ISplitter * splitter=
factory->CreateSplitter(); //多態(tài)new
splitter->split();
}
};
//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();
}
};
//ISplitterFactory.cpp
//抽象類
class ISplitter{
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
//工廠基類
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=0;
virtual ~SplitterFactory(){}
};
工廠結(jié)構(gòu):
要點總結(jié)
- Factory Method模式用于隔離類對象的使用者和具體類型之間的耦合關(guān)系揩徊。面對一個經(jīng)常變化的具體類型睦焕,緊耦合關(guān)系(new)會導(dǎo)致軟件的脆弱。
- Factory Method模式通過面向?qū)ο蟮氖址ㄑス埃瑢⑺獎?chuàng)建的具體對象工作延遲到子類垃喊,從而實現(xiàn)一種擴(kuò)展(而非更改)的策略,較好地解決了這種緊耦合關(guān)系袜炕。
- Factory Method模式解決“單個對象”的需求變化本谜。缺點在于要求創(chuàng)建方法/參數(shù)相同。
二 抽象工廠
動機(jī)
- 在軟件系統(tǒng)中偎窘,經(jīng)常面臨著“一系列相互依賴的對象”的創(chuàng)建工作乌助;同時溜在,由于需求的變化,往往存在更多系列對象的創(chuàng)建工作他托。
- 如何應(yīng)對這種變化掖肋?如何繞過常規(guī)的對象創(chuàng)建方法(new),提供一種“封裝機(jī)制”來避免客戶程序和這種“多系列具體對象創(chuàng)建工作”的緊耦合赏参?
模式定義:
提供一個接口志笼,讓該接口負(fù)責(zé)創(chuàng)建一系列“相關(guān)或者互相依賴的對象”,無需指定它們具體的類把篓∪依#————《設(shè)計模式》GoF
EmployeeDAO1:
class EmployeeDAO{
public:
vector<EmployeeDO> GetEmployees(){
SqlConnection* connection =
new SqlConnection();
connection->ConnectionString = "...";
SqlCommand* command =
new SqlCommand();
command->CommandText="...";
command->SetConnection(connection);
SqlDataReader* reader = command->ExecuteReader();
while (reader->Read()){
}
}
};
EmployeeDAO2:
//數(shù)據(jù)庫訪問有關(guān)的基類
class IDBConnection{
};
class IDBConnectionFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
};
class IDBCommand{
};
class IDBCommandFactory{
public:
virtual IDBCommand* CreateDBCommand()=0;
};
class IDataReader{
};
class IDataReaderFactory{
public:
virtual IDataReader* CreateDataReader()=0;
};
//支持SQL Server
class SqlConnection: public IDBConnection{
};
class SqlConnectionFactory:public IDBConnectionFactory{
};
class SqlCommand: public IDBCommand{
};
class SqlCommandFactory:public IDBCommandFactory{
};
class SqlDataReader: public IDataReader{
};
class SqlDataReaderFactory:public IDataReaderFactory{
};
//支持Oracle
class OracleConnection: public IDBConnection{
};
class OracleCommand: public IDBCommand{
};
class OracleDataReader: public IDataReader{
};
class EmployeeDAO{
IDBConnectionFactory* dbConnectionFactory;
IDBCommandFactory* dbCommandFactory;
IDataReaderFactory* dataReaderFactory;
public:
vector<EmployeeDO> GetEmployees(){
IDBConnection* connection =
dbConnectionFactory->CreateDBConnection();
connection->ConnectionString("...");
IDBCommand* command =
dbCommandFactory->CreateDBCommand();
command->CommandText("...");
command->SetConnection(connection); //關(guān)聯(lián)性
IDBDataReader* reader = command->ExecuteReader(); //關(guān)聯(lián)性
while (reader->Read()){
}
}
};
EmployeeDAO3:
//數(shù)據(jù)庫訪問有關(guān)的基類
class IDBConnection{
};
class IDBCommand{
};
class IDataReader{
};
class IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
//支持SQL Server
class SqlConnection: public IDBConnection{
};
class SqlCommand: public IDBCommand{
};
class SqlDataReader: public IDataReader{
};
class SqlDBFactory:public IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
//支持Oracle
class OracleConnection: public IDBConnection{
};
class OracleCommand: public IDBCommand{
};
class OracleDataReader: public IDataReader{
};
class EmployeeDAO{
IDBFactory* dbFactory;
public:
vector<EmployeeDO> GetEmployees(){
IDBConnection* connection =
dbFactory->CreateDBConnection();
connection->ConnectionString("...");
IDBCommand* command =
dbFactory->CreateDBCommand();
command->CommandText("...");
command->SetConnection(connection); //關(guān)聯(lián)性
IDBDataReader* reader = command->ExecuteReader(); //關(guān)聯(lián)性
while (reader->Read()){
}
}
};
抽象工廠結(jié)構(gòu):
要點總結(jié):
- 如果沒有應(yīng)對“多系列對象構(gòu)建”的需求變化,則沒有必要使用Abstract Factory模式韧掩,這時候使用簡單的工廠完全可以紊浩。
- “系列對象”指的是在某一特定系列下的對象之間有相互依賴、或作用的關(guān)系疗锐。不同系列的對象之間不能相互依賴坊谁。
- Abstract Factory模式主要在于應(yīng)對“新系列”的需求變動。其缺點在于難以應(yīng)對“新對象”的需求變動滑臊。
三 Prototype原型模式
動機(jī)
- 在軟件系統(tǒng)中呜袁,經(jīng)常面臨著“某些結(jié)構(gòu)復(fù)雜的對象”的創(chuàng)建工作;由于需求的變化简珠,這些對象經(jīng)常面臨著劇烈的變化阶界,但是它們卻擁有比較穩(wěn)定一致的接口。
- 如何應(yīng)對這種變化聋庵?如何向“客戶程序(使用這些對象的程序)”隔離出“這些易變對象”膘融,從而使得“依賴這些易變對象的客戶程序”不隨著需求改變而改變?
模式定義:
使用原型實例指定創(chuàng)建對象的種類祭玉,然后通過拷貝這些原型來創(chuàng)建新的對象氧映。——《設(shè)計模式》GoF
原型模式結(jié)構(gòu):
要點總結(jié):
- Prototype模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關(guān)系脱货,它同樣要求這些“易變類”擁有“穩(wěn)定的接口”岛都。
- Prototype模式對于“如何創(chuàng)建易變類的實體對象”采用“原型克隆”的方法來做,它使得我們可以非常靈活地動態(tài)創(chuàng)建“擁有某些穩(wěn)定接口”的新對象——所需工作僅僅是注冊一個新類的對象(即原型)振峻,然后在任何需要的地方Clone臼疫。
- Prototype模式中的Clone方法可以利用某些框架中的序列化來實現(xiàn)深拷貝。
四 Builder構(gòu)建器
動機(jī)
- 在軟件系統(tǒng)中扣孟,有時候面臨著“一個復(fù)雜對象”的創(chuàng)建工作烫堤,其通常由各個部分的子對象用一定的算法構(gòu)成;由于需求的變化,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化鸽斟,但是將它們組合在一起的算法卻相對穩(wěn)定拔创。
- 如何應(yīng)對這種變化?如何提供一種“封裝機(jī)制”來隔離出“復(fù)雜對象的各個部分”的變化富蓄,從而保持系統(tǒng)中的“穩(wěn)定構(gòu)建算法”不隨著需求改變而改變剩燥?
模式定義:
將一個復(fù)雜對象的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過程(穩(wěn)定)可以創(chuàng)建不同的表示(變化)立倍∶鸷欤————《設(shè)計模式》GoF
構(gòu)建器結(jié)構(gòu):
要點總結(jié):
- Builder模式主要用于“分步驟構(gòu)建一個復(fù)雜的對象”。在這其中“分步驟”是一個穩(wěn)定的算法帐萎,而復(fù)雜對象的各個部分則經(jīng)常變化比伏。
- 變化點在哪里胜卤,封裝哪里——Builder模式主要在于應(yīng)對“復(fù)雜對象各個部分”的頻繁需求變動疆导。其缺點在于難以應(yīng)對“分步驟構(gòu)建算法”的需求變動。
- 在Builder模式中葛躏,要注意不同語言中構(gòu)造器內(nèi)調(diào)用虛函數(shù)的差別(C++ vs. C#)澈段。
“接口隔離”模式
- 在組件構(gòu)建過程中,某些接口之間直接的依賴常常會帶來很多問題舰攒,甚至根本無法實現(xiàn)败富。采用添加一層間接(穩(wěn)定)接口,來隔離本來互相緊密關(guān)聯(lián)的接口是一種常見的解決方案摩窃。
- 典型模式
- Facade
- Proxy
- Adapter
- Mediator
五 Facade門面模式
動機(jī)
- 上述A方案的問題在于組件的客戶和組件中各種復(fù)雜的子系統(tǒng)有了過多的耦合兽叮,隨著外部客戶程序和各子系統(tǒng)的演化,這種過多的耦合面臨很多變化的挑戰(zhàn)猾愿。
- 如何簡化外部客戶程序和系統(tǒng)間的交互接口鹦聪?如何將外部客戶程序的演化和內(nèi)部子系統(tǒng)的變化之間的依賴相互解耦?
模式定義:
為子系統(tǒng)中的一組接口提供一個一致(穩(wěn)定)的界面Facade模式定義了一個高層接口蒂秘,這個接口使得這一子系統(tǒng)更加容易使用(復(fù)用)泽本。————《設(shè)計模式》GoF
門面模式結(jié)構(gòu):
要點總結(jié):
- 從客戶程序的角度來看姻僧,F(xiàn)acade模式簡化了整個組件系統(tǒng)的接口规丽,對于組件內(nèi)部與外部客戶程序來說,達(dá)到了一種“解耦”的效果——內(nèi)部子系統(tǒng)的任何變化不會影響到Facade接口的變化撇贺。
- Facade設(shè)計模式更注重從架構(gòu)的層次去看整個系統(tǒng)赌莺,而不是單個類的層次。Facade很多時候更是一種架構(gòu)設(shè)計模式松嘶。
- Facade設(shè)計模式并非一個集裝箱雄嚣,可以任意地放進(jìn)任何多個對象。Facade模式中組件的內(nèi)部應(yīng)該是“相互耦合關(guān)系比較大的一系列組件”,而不是一個簡單的功能集合缓升。
六 Proxy代理模式
動機(jī)
- 在面向?qū)ο笙到y(tǒng)中鼓鲁,有些對象由于某種原因(比如對象創(chuàng)建的開銷很大,或者某些操作需要安全控制港谊,或者需要進(jìn)程外的訪問等)骇吭,直接訪問會給使用者、或者系統(tǒng)結(jié)構(gòu)帶來很多麻煩歧寺。
- 如何在不失去透明操作對象的同時來管理/控制這些對象特有的復(fù)雜性燥狰?增加一層間接層是軟件開發(fā)中常見的解決方式。
模式定義:
為其他對象提供一種代理以控制(隔離斜筐,使用接口)對這個對象的訪問龙致。————《設(shè)計模式》GoF
要點總結(jié):
- “增加一層間接層”是軟件系統(tǒng)中對許多復(fù)雜問題的一種常見解決方法顷链。在面向?qū)ο笙到y(tǒng)中目代,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常見手段嗤练。
- 具體proxy設(shè)計模式的實現(xiàn)方式榛了、實現(xiàn)粒度都相差很大,有些可能對單個對象做細(xì)粒度的控制煞抬,如copy-on-write技術(shù)霜大,有些可能對組件模塊提供抽象代理層,在架構(gòu)層次對對象做proxy革答。
- Proxy并不一定要求保持接口完整的一致性战坤,只要能夠?qū)崿F(xiàn)間接控制,有時候損及一些透明性是可以接受的残拐。
七 Adapter適配器
動機(jī)
- 在軟件系統(tǒng)中途茫,由于應(yīng)用環(huán)境的變化,常常需要將“一些現(xiàn)存的對象”放在新的環(huán)境中應(yīng)用蹦骑,但是新環(huán)境要求的接口是這些現(xiàn)存對象所不滿足的慈省。
- 如何應(yīng)對這種“遷徙的變化”?如何既能利用現(xiàn)有對象的良好實現(xiàn)眠菇,同時又能滿足新的應(yīng)用環(huán)境所要求的接口边败?
模式定義:
將一個類的接口轉(zhuǎn)換成客戶希望的另一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作捎废⌒Υ埽————《設(shè)計模式》GoF
要點總結(jié):
- Adapter模式主要應(yīng)用于“希望復(fù)用一些現(xiàn)存的類,但是接口又與復(fù)用環(huán)境要求不一致的情況”登疗,在遺留代碼復(fù)用排截、類庫遷徙等方面非常有用嫌蚤。
- GoF 23定義了兩種Adapter模式的實現(xiàn)結(jié)構(gòu):對象適配器和類適配器。但類適配器采用“多繼承”的實現(xiàn)方式断傲,一般不推薦使用脱吱。對象適配器采用“對象組合”的方式,更符合松耦合精神认罩。
- Adapter模式可以實現(xiàn)的非常靈活箱蝠,不必拘泥于Gof23中定義的兩種結(jié)構(gòu)。例如垦垂,完全可以將Adapter模式中的“現(xiàn)存對象”作為新的接口方法參數(shù)宦搬,來達(dá)到適配的目的。
八 Mediator中介者
動機(jī)
- 在軟件構(gòu)建過程中劫拗,經(jīng)常會出現(xiàn)多個對象互相關(guān)聯(lián)交互的情況间校,對象之間常常會維持一種復(fù)雜的引用關(guān)系,如果遇到一些需求的更改页慷,這種直接的引用關(guān)系將面臨不斷的變化憔足。
- 在這種情況下,我們可使用一個“中介對象”來管理對象間的關(guān)聯(lián)關(guān)系差购,避免相互交互的對象之間的緊耦合引用關(guān)系四瘫,從而更好地抵御變化汉嗽。
模式定義:
用一個中介對象來封裝(封裝變化)一系列的對象交互欲逃。中介者使各對象不需要顯式的相互引用(編譯時依賴->運行時依賴),從而使其耦合松散(管理變化)饼暑,而且可以獨立地改變它們之間的交互稳析。————《設(shè)計模式》GoF
中介者結(jié)構(gòu):
要點總結(jié):
- 將多個對象間復(fù)雜的關(guān)聯(lián)關(guān)系解耦弓叛,Mediator模式將多個對象間的控制邏輯進(jìn)行集中管理彰居,變“多個對象互相關(guān)聯(lián)”為“多個對象和一個中介者關(guān)聯(lián)”,簡化了系統(tǒng)的維護(hù)撰筷,抵御了可能的變化陈惰。
- 隨著控制邏輯的復(fù)雜化,Mediator具體對象的實現(xiàn)可能想當(dāng)復(fù)雜毕籽。這時候可以對Mediator對象進(jìn)行分解處理抬闯。
- Facade模式是解耦系統(tǒng)間(單向)的對象關(guān)聯(lián)關(guān)系;Mediator模式是解耦系統(tǒng)內(nèi)各個對象之間(雙向)的關(guān)聯(lián)關(guān)系关筒。