工廠模式是我們最常用的實(shí)例化對(duì)象模式了,是用工廠方法代替new操作的一種模式。通常我們所說(shuō)的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。工廠模式在Java程序系統(tǒng)可以說(shuō)是隨處可見。因?yàn)楣S模式就相當(dāng)于創(chuàng)建實(shí)例對(duì)象的new,我們經(jīng)常要根據(jù)類Class生成實(shí)例對(duì)象,如A a=new A() 工廠模式也是用來(lái)創(chuàng)建實(shí)例對(duì)象的,所以以后new時(shí)就要多個(gè)心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會(huì)給你系統(tǒng)帶來(lái)更大的可擴(kuò)展性和盡量少的修改量。
工廠模式的作用
工廠模式(包括簡(jiǎn)單工廠模式、工廠方法模式和抽象工廠模式)到底有什么用,很多時(shí)候通過(guò)反射機(jī)制就可以很靈活地創(chuàng)建對(duì)象,為什么還要工廠?
與一個(gè)對(duì)象有關(guān)的原則有三類:
- 對(duì)象本身所具有的職責(zé)
- 創(chuàng)建對(duì)象的職責(zé)
- 使用對(duì)象的職責(zé)
對(duì)象本身的職責(zé)很好理解,就是對(duì)象自身所具有的一些數(shù)據(jù)和行為,可通過(guò)一些公開的方法來(lái)實(shí)現(xiàn)它的職責(zé)。在本文中,我們將簡(jiǎn)單討論一下對(duì)象的創(chuàng)建職責(zé)和使用職責(zé)。
在Java語(yǔ)言中,我們通常有以下幾種創(chuàng)建對(duì)象的方式:
- 使用new關(guān)鍵字直接創(chuàng)建對(duì)象;
- 通過(guò)反射機(jī)制創(chuàng)建對(duì)象;
- 通過(guò)clone()方法創(chuàng)建對(duì)象;
- 通過(guò)工廠類創(chuàng)建對(duì)象。
毫無(wú)疑問(wèn),在客戶端代碼中直接使用new關(guān)鍵字是最簡(jiǎn)單的一種創(chuàng)建對(duì)象的方式,但是它的靈活性較差,下面通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)加以說(shuō)明:
class LoginAction {
private UserDAO udao;
public LoginAction() {
udao = new JDBCUserDAO(); //創(chuàng)建對(duì)象
}
public void execute() {
//其他代碼
udao.findUserById(); //使用對(duì)象
//其他代碼
}
}
在LoginAction類中定義了一個(gè)UserDAO類型的對(duì)象udao,在LoginAction的構(gòu)造函數(shù)中創(chuàng)建了JDBCUserDAO類型的udao對(duì)象,并在execute()方法中調(diào)用了udao對(duì)象的findUserById()方法,這段代碼看上去并沒(méi)有什么問(wèn)題。下面我們來(lái)分析一下LoginAction和UserDAO之間的關(guān)系,LoginAction類負(fù)責(zé)創(chuàng)建了一個(gè)UserDAO子類的對(duì)象并使用UserDAO的方法來(lái)完成相應(yīng)的業(yè)務(wù)處理,也就是說(shuō)LoginAction即負(fù)責(zé)udao的創(chuàng)建又負(fù)責(zé)udao的使用,創(chuàng)建對(duì)象和使用對(duì)象的職責(zé)耦合在一起,這樣的設(shè)計(jì)會(huì)導(dǎo)致一個(gè)很嚴(yán)重的問(wèn)題:如果在LoginAction中希望能夠使用UserDAO的另一個(gè)子類如HibernateUserDAO類型的對(duì)象,必須修改LoginAction類的源代碼,違反了“開閉原則”。如何解決該問(wèn)題?
最常用的一種解決方法是將udao對(duì)象的創(chuàng)建職責(zé)從LoginAction類中移除,在LoginAction類之外創(chuàng)建對(duì)象,那么誰(shuí)來(lái)負(fù)責(zé)創(chuàng)建UserDAO對(duì)象呢?答案是:工廠類。通過(guò)引入工廠類,客戶類(如LoginAction)不涉及對(duì)象的創(chuàng)建,對(duì)象的創(chuàng)建者也不會(huì)涉及對(duì)象的使用。引入工廠類UserDAOFactory之后的結(jié)構(gòu)如圖1所示:

工廠類的引入將降低因?yàn)楫a(chǎn)品或工廠類改變所造成的維護(hù)工作量。如果UserDAO的某個(gè)子類的構(gòu)造函數(shù)發(fā)生改變或者要需要添加或移除不同的子類,只要維護(hù)UserDAOFactory的代碼,而不會(huì)影響到LoginAction;如果UserDAO的接口發(fā)生改變,例如添加、移除方法或改變方法名,只需要修改LoginAction,不會(huì)給UserDAOFactory帶來(lái)任何影響。
在所有的工廠模式中,我們都強(qiáng)調(diào)一點(diǎn):兩個(gè)類A和B之間的關(guān)系應(yīng)該僅僅是A創(chuàng)建B或是A使用B,而不能兩種關(guān)系都有。將對(duì)象的創(chuàng)建和使用分離,也使得系統(tǒng)更加符合“單一職責(zé)原則”,有利于對(duì)功能的復(fù)用和系統(tǒng)的維護(hù)。
此外,將對(duì)象的創(chuàng)建和使用分離還有一個(gè)好處:防止用來(lái)實(shí)例化一個(gè)類的數(shù)據(jù)和代碼在多個(gè)類中到處都是,可以將有關(guān)創(chuàng)建的知識(shí)搬移到一個(gè)工廠類中,這在Joshua Kerievsky的《重構(gòu)與模式》一書中有專門的一節(jié)來(lái)進(jìn)行介紹。因?yàn)橛袝r(shí)候我們創(chuàng)建一個(gè)對(duì)象不只是簡(jiǎn)單調(diào)用其構(gòu)造函數(shù),還需要設(shè)置一些參數(shù),可能還需要配置環(huán)境,如果將這些代碼散落在每一個(gè)創(chuàng)建對(duì)象的客戶類中,勢(shì)必會(huì)出現(xiàn)代碼重復(fù)、創(chuàng)建蔓延的問(wèn)題,而這些客戶類其實(shí)無(wú)須承擔(dān)對(duì)象的創(chuàng)建工作,它們只需使用已創(chuàng)建好的對(duì)象就可以了。此時(shí),可以引入工廠類來(lái)封裝對(duì)象的創(chuàng)建邏輯和客戶代碼的實(shí)例化/配置選項(xiàng)。
使用工廠類還有一個(gè)“不是特別明顯的”優(yōu)點(diǎn),一個(gè)類可能擁有多個(gè)構(gòu)造函數(shù),而在Java、C#等語(yǔ)言中構(gòu)造函數(shù)名字都與類名相同,客戶端只能通過(guò)傳入不同的參數(shù)來(lái)調(diào)用不同的構(gòu)造函數(shù)創(chuàng)建對(duì)象,從構(gòu)造函數(shù)和參數(shù)列表中也許大家根本不了解不同構(gòu)造函數(shù)所構(gòu)造的產(chǎn)品的差異。但如果將對(duì)象的創(chuàng)建過(guò)程封裝在工廠類中,我們可以提供一系列名字完全不同的工廠方法,每一個(gè)工廠方法對(duì)應(yīng)一個(gè)構(gòu)造函數(shù),客戶端可以以一種更加可讀、易懂的方式來(lái)創(chuàng)建對(duì)象,而且,從一組工廠方法中選擇一個(gè)意義明確的工廠方法,比從一組名稱相同參數(shù)不同的構(gòu)造函數(shù)中選擇一個(gè)構(gòu)造函數(shù)要方便很多。
那么,有人可能會(huì)問(wèn),是否需要為設(shè)計(jì)中的每一個(gè)類都配備一個(gè)工廠類?答案是:具體情況具體分析。如果產(chǎn)品類很簡(jiǎn)單,而且不存在太多變數(shù),其構(gòu)造過(guò)程也很簡(jiǎn)單,此時(shí)無(wú)須為其提供工廠類,直接在使用之前實(shí)例化即可,例如Java語(yǔ)言中的String類,我們就無(wú)須為它專門提供一個(gè)StringFactory,這樣做反而有點(diǎn)像殺雞用牛刀,大材小用,而且會(huì)導(dǎo)致工廠泛濫,增加系統(tǒng)的復(fù)雜度。
工廠模式是為了將對(duì)象的創(chuàng)建和使用分離,也使得系統(tǒng)更加符合“單一職責(zé)原則”,有利于對(duì)功能的復(fù)用和系統(tǒng)的維護(hù),防止用來(lái)實(shí)例化一個(gè)類的數(shù)據(jù)和代碼在多個(gè)類中到處都是,可以將有關(guān)創(chuàng)建的知識(shí)搬移到一個(gè)工廠類中。
轉(zhuǎn)載:創(chuàng)建對(duì)象與使用對(duì)象——談?wù)劰S的作用
簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式(Simple Factory Pattern):定義一個(gè)工廠類,它可以根據(jù)參數(shù)的不同返回不同類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。因?yàn)樵诤?jiǎn)單工廠模式中用于創(chuàng)建實(shí)例的方法是靜態(tài)(static)方法,因此簡(jiǎn)單工廠模式又被稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。
簡(jiǎn)單工廠模式的要點(diǎn)在于:當(dāng)你需要什么,只需要傳入一個(gè)正確的參數(shù),就可以獲取你所需要的對(duì)象,而無(wú)須知道其創(chuàng)建細(xì)節(jié)。簡(jiǎn)單工廠模式結(jié)構(gòu)比較簡(jiǎn)單,其核心是工廠類的設(shè)計(jì), 其結(jié)構(gòu)如下圖所示。

在簡(jiǎn)單工廠模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- Factory(工廠角色):工廠角色即工廠類,它是簡(jiǎn)單工廠模式的核心,負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有產(chǎn)品實(shí)例的內(nèi)部邏輯;工廠類可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對(duì)象;在工廠類中提供了靜態(tài)的工廠方法factoryMethod(),它的返回類型為抽象產(chǎn)品類型Product。
- Product(抽象產(chǎn)品角色):它是工廠類所創(chuàng)建的所有對(duì)象的父類,封裝了各種產(chǎn)品對(duì)象的公有方法,它的引入將提高系統(tǒng)的靈活性,使得在工廠類中只需定義一個(gè)通用的工廠方法,因?yàn)樗袆?chuàng)建的具體產(chǎn)品對(duì)象都是其子類對(duì)象。
- ConcreteProduct(具體產(chǎn)品角色):它是簡(jiǎn)單工廠模式的創(chuàng)建目標(biāo),所有被創(chuàng)建的對(duì)象都充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例。每一個(gè)具體產(chǎn)品角色都繼承了抽象產(chǎn)品角色,需要實(shí)現(xiàn)在抽象產(chǎn)品中聲明的抽象方法。
在簡(jiǎn)單工廠模式中,客戶端通過(guò)工廠類來(lái)創(chuàng)建一個(gè)產(chǎn)品類的實(shí)例,而無(wú)須直接使用new關(guān)鍵字來(lái)創(chuàng)建對(duì)象,它是工廠模式家族中最簡(jiǎn)單的一員。
在使用簡(jiǎn)單工廠模式時(shí),首先需要對(duì)產(chǎn)品類進(jìn)行重構(gòu),不能設(shè)計(jì)一個(gè)包羅萬(wàn)象的產(chǎn)品類,而需根據(jù)實(shí)際情況設(shè)計(jì)一個(gè)產(chǎn)品層次結(jié)構(gòu),將所有產(chǎn)品類公共的代碼移至抽象產(chǎn)品類,并在抽象產(chǎn)品類中聲明一些抽象方法,以供不同的具體產(chǎn)品類來(lái)實(shí)現(xiàn),典型的抽象產(chǎn)品類代碼如下所示:
abstract class Product {
//所有產(chǎn)品類的公共業(yè)務(wù)方法
public void methodSame() {
//公共方法的實(shí)現(xiàn)
}
//聲明抽象業(yè)務(wù)方法
public abstract void methodDiff();
}
在具體產(chǎn)品類中實(shí)現(xiàn)了抽象產(chǎn)品類中聲明的抽象業(yè)務(wù)方法,不同的具體產(chǎn)品類可以提供不同的實(shí)現(xiàn),典型的具體產(chǎn)品類代碼如下所示:
class ConcreteProduct extends Product {
//實(shí)現(xiàn)業(yè)務(wù)方法
public void methodDiff() {
//業(yè)務(wù)方法的實(shí)現(xiàn)
}
}
簡(jiǎn)單工廠模式的核心是工廠類,在沒(méi)有工廠類之前,客戶端一般會(huì)使用new關(guān)鍵字來(lái)直接創(chuàng)建產(chǎn)品對(duì)象,而在引入工廠類之后,客戶端可以通過(guò)工廠類來(lái)創(chuàng)建產(chǎn)品,在簡(jiǎn)單工廠模式中,工廠類提供了一個(gè)靜態(tài)工廠方法供客戶端使用,根據(jù)所傳入的參數(shù)不同可以創(chuàng)建不同的產(chǎn)品對(duì)象,典型的工廠類代碼如下所示:
class Factory {
//靜態(tài)工廠方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化設(shè)置product
}
else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化設(shè)置product
}
return product;
}
}
在客戶端代碼中,我們通過(guò)調(diào)用工廠類的工廠方法即可得到產(chǎn)品對(duì)象,典型代碼如下所示:
class Client {
public static void main(String args[]) {
Product product;
product = Factory.getProduct("A"); //通過(guò)工廠類創(chuàng)建產(chǎn)品對(duì)象
product.methodSame();
product.methodDiff();
}
}
簡(jiǎn)單工廠模式總結(jié):
簡(jiǎn)單工廠模式提供了專門的工廠類用于創(chuàng)建對(duì)象,將對(duì)象的創(chuàng)建和對(duì)象的使用分離開,它作為一種最簡(jiǎn)單的工廠模式在軟件開發(fā)中得到了較為廣泛的應(yīng)用。
-
優(yōu)點(diǎn):
- 工廠類包含必要的判斷邏輯,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例,客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的職責(zé),而僅僅“消費(fèi)”產(chǎn)品,簡(jiǎn)單工廠模式實(shí)現(xiàn)了對(duì)象創(chuàng)建和使用的分離。
- 客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類的類名,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可,對(duì)于一些復(fù)雜的類名,通過(guò)簡(jiǎn)單工廠模式可以在一定程度減少使用者的記憶量。
- 通過(guò)引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類,在一定程度上提高了系統(tǒng)的靈活性。
-
缺點(diǎn):
- 由于工廠類集中了所有產(chǎn)品的創(chuàng)建邏輯,職責(zé)過(guò)重,一旦不能正常工作,整個(gè)系統(tǒng)都要受到影響。
- 使用簡(jiǎn)單工廠模式勢(shì)必會(huì)增加系統(tǒng)中類的個(gè)數(shù)(引入了新的工廠類),增加了系統(tǒng)的復(fù)雜度和理解難度。
- 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
- 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
-
適用場(chǎng)景:
- 工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少,由于創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過(guò)復(fù)雜。
- 工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少,由于創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過(guò)復(fù)雜。
轉(zhuǎn)載:工廠三兄弟之簡(jiǎn)單工廠模式(二)
工廠方法模式
簡(jiǎn)單工廠模式雖然簡(jiǎn)單,但存在一個(gè)很嚴(yán)重的問(wèn)題。當(dāng)系統(tǒng)中需要引入新產(chǎn)品時(shí),由于靜態(tài)工廠方法通過(guò)所傳入?yún)?shù)的不同來(lái)創(chuàng)建不同的產(chǎn)品,這必定要修改工廠類的源代碼,將違背“開閉原則”,如何實(shí)現(xiàn)增加新產(chǎn)品而不影響已有代碼?工廠方法模式應(yīng)運(yùn)而生.
在工廠方法模式中,我們不再提供一個(gè)統(tǒng)一的工廠類來(lái)創(chuàng)建所有的產(chǎn)品對(duì)象,而是針對(duì)不同的產(chǎn)品提供不同的工廠,系統(tǒng)提供一個(gè)與產(chǎn)品等級(jí)結(jié)構(gòu)對(duì)應(yīng)的工廠等級(jí)結(jié)構(gòu)。
工廠方法模式(Factory Method Pattern):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定將哪一個(gè)類實(shí)例化。工廠方法模式讓一個(gè)類的實(shí)例化延遲到其子類。工廠方法模式又簡(jiǎn)稱為工廠模式(Factory Pattern),又可稱作虛擬構(gòu)造器模式(Virtual Constructor Pattern)或多態(tài)工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種類創(chuàng)建型模式。
工廠方法模式提供一個(gè)抽象工廠接口來(lái)聲明抽象工廠方法,而由其子類來(lái)具體實(shí)現(xiàn)工廠方法,創(chuàng)建具體的產(chǎn)品對(duì)象。工廠方法模式結(jié)構(gòu)如圖所示:

在工廠方法模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- Product(抽象產(chǎn)品):它是定義產(chǎn)品的接口,是工廠方法模式所創(chuàng)建對(duì)象的超類型,也就是產(chǎn)品對(duì)象的公共父類。
- ConcreteProduct(具體產(chǎn)品):它實(shí)現(xiàn)了抽象產(chǎn)品接口,某種類型的具體產(chǎn)品由專門的具體工廠創(chuàng)建,具體工廠和具體產(chǎn)品之間一一對(duì)應(yīng)。
- Factory(抽象工廠):在抽象工廠類中,聲明了工廠方法(Factory Method),用于返回一個(gè)產(chǎn)品。抽象工廠是工廠方法模式的核心,所有創(chuàng)建對(duì)象的工廠類都必須實(shí)現(xiàn)該接口。
- ConcreteFactory(具體工廠):它是抽象工廠類的子類,實(shí)現(xiàn)了抽象工廠中定義的工廠方法,并可由客戶端調(diào)用,返回一個(gè)具體產(chǎn)品類的實(shí)例。
與簡(jiǎn)單工廠模式相比,工廠方法模式最重要的區(qū)別是引入了抽象工廠角色,抽象工廠可以是接口,也可以是抽象類或者具體類,其典型代碼如下所示:
interface Factory {
public Product factoryMethod();
}
在實(shí)際使用時(shí),具體工廠類在實(shí)現(xiàn)工廠方法時(shí)除了創(chuàng)建具體產(chǎn)品對(duì)象之外,還可以負(fù)責(zé)產(chǎn)品對(duì)象的初始化工作以及一些資源和環(huán)境配置工作,例如連接數(shù)據(jù)庫(kù)、創(chuàng)建文件等。
在客戶端代碼中,只需關(guān)心工廠類即可,不同的具體工廠可以創(chuàng)建不同的產(chǎn)品,典型的客戶端類代碼片段如下所示:
Factory factory;
factory = new ConcreteFactory(); //可以結(jié)合配置文件和反射機(jī)制來(lái)實(shí)現(xiàn)
Product product;
product = factory.factoryMethod();
可以通過(guò)配置文件來(lái)存儲(chǔ)具體工廠類ConcreteFactory的類名,更換新的具體工廠時(shí)無(wú)須修改源代碼,系統(tǒng)擴(kuò)展更為方便。
工廠方法模式總結(jié)
-
優(yōu)點(diǎn)
- 在工廠方法模式中,工廠方法用來(lái)創(chuàng)建客戶所需要的產(chǎn)品,同時(shí)還向客戶隱藏了哪種具體產(chǎn)品類將被實(shí)例化這一細(xì)節(jié),用戶只需要關(guān)心所需產(chǎn)品對(duì)應(yīng)的工廠,無(wú)須關(guān)心創(chuàng)建細(xì)節(jié),甚至無(wú)須知道具體產(chǎn)品類的類名。
- 基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計(jì)是工廠方法模式的關(guān)鍵。它能夠讓工廠可以自主確定創(chuàng)建何種產(chǎn)品對(duì)象,而如何創(chuàng)建這個(gè)對(duì)象的細(xì)節(jié)則完全封裝在具體工廠內(nèi)部。工廠方法模式之所以又被稱為多態(tài)工廠模式,就正是因?yàn)樗械木唧w工廠類都具有同一抽象父類。
- 使用工廠方法模式的另一個(gè)優(yōu)點(diǎn)是在系統(tǒng)中加入新產(chǎn)品時(shí),無(wú)須修改抽象工廠和抽象產(chǎn)品提供的接口,無(wú)須修改客戶端,也無(wú)須修改其他的具體工廠和具體產(chǎn)品,而只要添加一個(gè)具體工廠和具體產(chǎn)品就可以了,這樣,系統(tǒng)的可擴(kuò)展性也就變得非常好,完全符合“開閉原則”。
-
缺點(diǎn)
- 在添加新產(chǎn)品時(shí),需要編寫新的具體產(chǎn)品類,而且還要提供與之對(duì)應(yīng)的具體工廠類,系統(tǒng)中類的個(gè)數(shù)將成對(duì)增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,有更多的類需要編譯和運(yùn)行,會(huì)給系統(tǒng)帶來(lái)一些額外的開銷。
- 由于考慮到系統(tǒng)的可擴(kuò)展性,需要引入抽象層,在客戶端代碼中均使用抽象層進(jìn)行定義,增加了系統(tǒng)的抽象性和理解難度,且在實(shí)現(xiàn)時(shí)可能需要用到DOM、反射等技術(shù),增加了系統(tǒng)的實(shí)現(xiàn)難度。
-
適用場(chǎng)景
- 客戶端不知道它所需要的對(duì)象的類。在工廠方法模式中,客戶端不需要知道具體產(chǎn)品類的類名,只需要知道所對(duì)應(yīng)的工廠即可,具體的產(chǎn)品對(duì)象由具體工廠類創(chuàng)建,可將具體工廠類的類名存儲(chǔ)在配置文件或數(shù)據(jù)庫(kù)中。
- 抽象工廠類通過(guò)其子類來(lái)指定創(chuàng)建哪個(gè)對(duì)象。在工廠方法模式中,對(duì)于抽象工廠類只需要提供一個(gè)創(chuàng)建產(chǎn)品的接口,而由其子類來(lái)確定具體要?jiǎng)?chuàng)建的對(duì)象,利用面向?qū)ο蟮亩鄳B(tài)性和里氏代換原則,在程序運(yùn)行時(shí),子類對(duì)象將覆蓋父類對(duì)象,從而使得系統(tǒng)更容易擴(kuò)展。
抽象工廠模式
抽象工廠模式(Abstract Factory Pattern):提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無(wú)須指定它們具體的類。抽象工廠模式又稱為Kit模式,它是一種對(duì)象創(chuàng)建型模式。
在抽象工廠模式中,每一個(gè)具體工廠都提供了多個(gè)工廠方法用于產(chǎn)生多種不同類型的產(chǎn)品,這些產(chǎn)品構(gòu)成了一個(gè)產(chǎn)品族,抽象工廠模式結(jié)構(gòu)如圖所示:

在抽象工廠模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- AbstractFactory(抽象工廠):它聲明了一組用于創(chuàng)建一族產(chǎn)品的方法,每一個(gè)方法對(duì)應(yīng)一種產(chǎn)品。
- ConcreteFactory(具體工廠):它實(shí)現(xiàn)了在抽象工廠中聲明的創(chuàng)建產(chǎn)品的方法,生成一組具體產(chǎn)品,這些產(chǎn)品構(gòu)成了一個(gè)產(chǎn)品族,每一個(gè)產(chǎn)品都位于某個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)中。
- AbstractProduct(抽象產(chǎn)品):它為每種產(chǎn)品聲明接口,在抽象產(chǎn)品中聲明了產(chǎn)品所具有的業(yè)務(wù)方法。
- ConcreteProduct(具體產(chǎn)品):它定義具體工廠生產(chǎn)的具體產(chǎn)品對(duì)象,實(shí)現(xiàn)抽象產(chǎn)品接口中聲明的業(yè)務(wù)方法。
在抽象工廠中聲明了多個(gè)工廠方法,用于創(chuàng)建不同類型的產(chǎn)品,抽象工廠可以是接口,也可以是抽象類或者具體類,其典型代碼如下所示:
class ConcreteFactory1 extends AbstractFactory {
//工廠方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
//工廠方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
……
}
與工廠方法模式一樣,抽象工廠模式也可為每一種產(chǎn)品提供一組重載的工廠方法,以不同的方式對(duì)產(chǎn)品對(duì)象進(jìn)行創(chuàng)建。
具體工廠實(shí)現(xiàn)了抽象工廠,每一個(gè)具體的工廠方法可以返回一個(gè)特定的產(chǎn)品對(duì)象,而同一個(gè)具體工廠所創(chuàng)建的產(chǎn)品對(duì)象構(gòu)成了一個(gè)產(chǎn)品族。
開閉原則”的傾斜性
在抽象工廠模式中,增加新的產(chǎn)品族很方便,但是增加新的產(chǎn)品等級(jí)結(jié)構(gòu)很麻煩,抽象工廠模式的這種性質(zhì)稱為“開閉原則”的傾斜性。“開閉原則”要求系統(tǒng)對(duì)擴(kuò)展開放,對(duì)修改封閉,通過(guò)擴(kuò)展達(dá)到增強(qiáng)其功能的目的,對(duì)于涉及到多個(gè)產(chǎn)品族與多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)的系統(tǒng),其功能增強(qiáng)包括兩方面:
- 增加產(chǎn)品族:對(duì)于增加新的產(chǎn)品族,抽象工廠模式很好地支持了“開閉原則”,只需要增加具體產(chǎn)品并對(duì)應(yīng)增加一個(gè)新的具體工廠,對(duì)已有代碼無(wú)須做任何修改。
- 加新的產(chǎn)品等級(jí)結(jié)構(gòu):對(duì)于增加新的產(chǎn)品等級(jí)結(jié)構(gòu),需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產(chǎn)新產(chǎn)品的方法,違背了“開閉原則”。
正因?yàn)槌橄蠊S模式存在“開閉原則”的傾斜性,它以一種傾斜的方式來(lái)滿足“開閉原則”,為增加新產(chǎn)品族提供方便,但不能為增加新產(chǎn)品結(jié)構(gòu)提供這樣的方便,因此要求設(shè)計(jì)人員在設(shè)計(jì)之初就能夠全面考慮,不會(huì)在設(shè)計(jì)完成之后向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu),也不會(huì)刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu),否則將會(huì)導(dǎo)致系統(tǒng)出現(xiàn)較大的修改,為后續(xù)維護(hù)工作帶來(lái)諸多麻煩。
抽象工廠模式總結(jié)
-
優(yōu)點(diǎn)
- 抽象工廠模式隔離了具體類的生成,使得客戶并不需要知道什么被創(chuàng)建。由于這種隔離,更換一個(gè)具體工廠就變得相對(duì)容易,所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實(shí)例,就可以在某種程度上改變整個(gè)軟件系統(tǒng)的行為。
- 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí),它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象。
- 增加新的產(chǎn)品族很方便,無(wú)須修改已有系統(tǒng),符合“開閉原則”。
-
缺點(diǎn)
- 增加新的產(chǎn)品等級(jí)結(jié)構(gòu)麻煩,需要對(duì)原有系統(tǒng)進(jìn)行較大的修改,甚至需要修改抽象層代碼,這顯然會(huì)帶來(lái)較大的不便,違背了“開閉原則”。
-
適用場(chǎng)景
- 一個(gè)系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié),這對(duì)于所有類型的工廠模式都是很重要的,用戶無(wú)須關(guān)心對(duì)象的創(chuàng)建過(guò)程,將對(duì)象的創(chuàng)建和使用解耦。
- 系統(tǒng)中有多于一個(gè)的產(chǎn)品族,而每次只使用其中某一產(chǎn)品族。可以通過(guò)配置文件等方式來(lái)使得用戶可以動(dòng)態(tài)改變產(chǎn)品族,也可以很方便地增加新的產(chǎn)品族。
- 屬于同一個(gè)產(chǎn)品族的產(chǎn)品將在一起使用,這一約束必須在系統(tǒng)的設(shè)計(jì)中體現(xiàn)出來(lái)。同一個(gè)產(chǎn)品族中的產(chǎn)品可以是沒(méi)有任何關(guān)系的對(duì)象,但是它們都具有一些共同的約束,如同一操作系統(tǒng)下的按鈕和文本框,按鈕與文本框之間沒(méi)有直接關(guān)系,但它們都是屬于某一操作系統(tǒng)的,此時(shí)具有一個(gè)共同的約束條件:操作系統(tǒng)的類型。
- 產(chǎn)品等級(jí)結(jié)構(gòu)穩(wěn)定,設(shè)計(jì)完成之后,不會(huì)向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu)或者刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu)。