【JS】708- 創(chuàng)建對(duì)象的最佳方式是什么?
在現(xiàn)實(shí)生活中,工廠是負(fù)責(zé)生產(chǎn)產(chǎn)品的,比如牛奶、面包或禮物等,這些產(chǎn)品滿足了我們?nèi)粘5纳硇枨蟆?strong style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;">(文末有精彩送書活動(dòng)喲)

作為一名 Web 軟件開發(fā)工程師,在軟件系統(tǒng)的設(shè)計(jì)與開發(fā)過程中,我們可以利用設(shè)計(jì)模式來提高代碼的可重用性、可擴(kuò)展性和可維護(hù)性。在眾多設(shè)計(jì)模式當(dāng)中,有一種被稱為工廠模式的設(shè)計(jì)模式,它提供了創(chuàng)建對(duì)象的最佳方式。
工廠模式可以分為:簡單工廠模式、工廠方法模式和抽象工廠模式。本文阿寶哥將詳細(xì)介紹抽象工廠模式,不過在介紹該模式之前,我們先來回顧一下簡單工廠模式和工廠方法模式。
一、簡單工廠模式
1.1 簡單工廠模式簡介
簡單工廠模式又叫 靜態(tài)方法模式,因?yàn)楣S類中定義了一個(gè)靜態(tài)方法用于創(chuàng)建對(duì)象。簡單工廠讓使用者不用知道具體的參數(shù)就可以創(chuàng)建出所需的 ”產(chǎn)品“ 類,即使用者可以直接消費(fèi)產(chǎn)品而不需要知道產(chǎn)品的具體生產(chǎn)細(xì)節(jié)。
對(duì)于剛接觸簡單工廠模式的小伙伴來說,看到以上的描述可能會(huì)覺得有點(diǎn)抽象。因此為了讓小伙伴更好地理解簡單工廠模式,阿寶哥以用戶買車為例,來介紹一下 BMW 工廠如何使用簡單工廠模式來生產(chǎn)小汽車。

在上圖中,阿寶哥模擬了用戶購車的流程,小王和小秦分別向 BMW 工廠訂購了 BMW730 和 BMW840 型號(hào)的車型,接著工廠會(huì)先判斷用戶選擇的車型,然后按照對(duì)應(yīng)的模型進(jìn)行生產(chǎn)并在生產(chǎn)完成后交付給用戶。接下來,我們來看一下如何使用簡單工廠來描述 BMW 工廠生產(chǎn)指定型號(hào)車子的過程。
class?BMWFactory?{
??public?static?produceBMW(model:?"730"?|?"840"):?BMW?{
????if?(model?===?"730")?{
??????return?new?BMW730();
????}?else?{
??????return?new?BMW840();
????}
??}
}
const?bmw730?=?BMWFactory.produceBMW("730");
const?bmw840?=?BMWFactory.produceBMW("840");
在以上代碼中,我們定義一個(gè) BMWFactory 類,該類提供了一個(gè)靜態(tài)的 produceBMW() 方法,用于根據(jù)不同的模型參數(shù)來創(chuàng)建不同型號(hào)的車子??赐暌陨系拇a,相信很多小伙伴會(huì)覺得很熟悉,因?yàn)樵谝恍╉?xiàng)目中就使用了簡單工廠模式。該模式很簡單,在滿足以下條件下可以考慮使用簡單工廠模式:
工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少:由于創(chuàng)建的對(duì)象比較少,不會(huì)造成工廠方法中業(yè)務(wù)邏輯過于復(fù)雜。 客戶端只需知道傳入工廠類靜態(tài)方法的參數(shù),而不需要關(guān)心創(chuàng)建對(duì)象的細(xì)節(jié)。
了解完簡單工廠模式的應(yīng)用場景,接下來我們來看一下該模式的優(yōu)缺點(diǎn)。
1.2 簡單工廠模式優(yōu)缺點(diǎn)
1.2.1 優(yōu)點(diǎn)
將創(chuàng)建實(shí)例與使用實(shí)例的任務(wù)分開,使用者不必關(guān)心對(duì)象是如何創(chuàng)建的,實(shí)現(xiàn)了系統(tǒng)的解耦; 客戶端無須知道所創(chuàng)建的具體產(chǎn)品類的類名,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可。
1.2.2 缺點(diǎn)
由于工廠類集中了所有產(chǎn)品創(chuàng)建邏輯,一旦不能正常工作,整個(gè)系統(tǒng)都要受到影響。 系統(tǒng)擴(kuò)展困難,一旦添加新產(chǎn)品就不得不修改工廠邏輯,在產(chǎn)品類型較多時(shí),也有可能造成工廠邏輯過于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)。
在一些實(shí)際的項(xiàng)目中,為了避免簡單工廠模式帶來的問題,我們可以考慮使用工廠方法模式。
二、工廠方法模式
2.1 工廠方法模式簡介
工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫多態(tài)工廠(Polymorphic Factory)模式,它屬于創(chuàng)建型模式。
在工廠方法模式中,工廠父類負(fù)責(zé)定義創(chuàng)建產(chǎn)品對(duì)象的公共接口,而工廠子類則負(fù)責(zé)生成具體的產(chǎn)品對(duì)象, 這樣做的目的是將產(chǎn)品類的實(shí)例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應(yīng)該實(shí)例化哪一個(gè)具體產(chǎn)品類。

在上圖中,阿寶哥模擬了用戶購車的流程,小王和小秦分別向 BMW 730 和 BMW 840 工廠訂購了 BMW730 和 BMW840 型號(hào)的車子,接著工廠按照對(duì)應(yīng)的模型進(jìn)行生產(chǎn)并在生產(chǎn)完成后交付給用戶。同樣,我們來看一下如何使用工廠方法來描述 BMW 工廠生產(chǎn)指定型號(hào)車子的過程。
interface?BMWFactory?{
??produceBMW():?BMW;
}
class?BMW730Factory?implements?BMWFactory?{
??produceBMW():?BMW?{
????return?new?BMW730();
??}
}
class?BMW840Factory?implements?BMWFactory?{
??produceBMW():?BMW?{
????return?new?BMW840();
??}
}
const?bmw730Factory?=?new?BMW730Factory();
const?bmw840Factory?=?new?BMW840Factory();
const?bmw730?=?bmw730Factory.produceBMW();
const?bmw840?=?bmw840Factory.produceBMW();
在以上代碼中,我們分別創(chuàng)建了 BMW730Factory 和 BMW840Factory 兩個(gè)工廠類,然后使用這兩個(gè)類的實(shí)例來生產(chǎn)不同型號(hào)的車子。相比前面的簡單工廠模式,工廠方法模式通過創(chuàng)建不同的工廠來生產(chǎn)不同的產(chǎn)品。下面我們來看一下工廠方法有哪些優(yōu)缺點(diǎn)。
2.2 工廠方法優(yōu)缺點(diǎn)
2.2.1 優(yōu)點(diǎn)
在系統(tǒng)中加入新產(chǎn)品時(shí),無須修改抽象工廠和抽象產(chǎn)品提供的接口,只要添加一個(gè)具體工廠和具體產(chǎn)品就可以了。這樣,系統(tǒng)的可擴(kuò)展性也就變得非常好,更加符合 “開閉原則”。而簡單工廠模式需要修改工廠類的判斷邏輯。 符合單一職責(zé)的原則,即每個(gè)具體工廠類只負(fù)責(zé)創(chuàng)建對(duì)應(yīng)的產(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工廠類都具有同一抽象父類。
2.2.2 缺點(diǎn)
在添加新產(chǎn)品時(shí),需要編寫新的具體產(chǎn)品類,而且還要提供與之對(duì)應(yīng)的具體工廠類,系統(tǒng)中類的個(gè)數(shù)將成對(duì)增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,有更多的類需要編譯和運(yùn)行,會(huì)給系統(tǒng)帶來一些額外的開銷。 一個(gè)具體工廠只能創(chuàng)建一種具體產(chǎn)品。
這里我們知道在工廠方法模式中,一個(gè)具體工廠只能創(chuàng)建一種具體產(chǎn)品。很明顯這限制該模式的使用場景,那么我們是否能突破該限制呢?答案是可以的。下面我們來介紹抽象工廠模式。
三、抽象工廠模式
3.1 抽象工廠簡介
抽象工廠模式(Abstract Factory Pattern):提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無須指定它們具體的類。
在工廠方法模式中具體工廠負(fù)責(zé)生產(chǎn)具體的產(chǎn)品,每一個(gè)具體工廠對(duì)應(yīng)一種具體產(chǎn)品,工廠方法也具有唯一性,一般情況下,一個(gè)具體工廠中只有一個(gè)工廠方法或者一組重載的工廠方法。但是有時(shí)候我們需要一個(gè)工廠可以提供多個(gè)產(chǎn)品對(duì)象,而不是單一的產(chǎn)品對(duì)象。

在上圖中,阿寶哥模擬了用戶購車的流程,小王向 BMW 工廠訂購了 BMW730,工廠按照 730 對(duì)應(yīng)的模型進(jìn)行生產(chǎn)并在生產(chǎn)完成后交付給小王。而小秦向 BMW 工廠訂購了 BMW840,工廠按照 840 對(duì)應(yīng)的模型進(jìn)行生產(chǎn)并在生產(chǎn)完成后交付給小秦。接下來,我們來看一下如何使用抽象工廠來描述上述的購車過程。
3.2 抽象工廠實(shí)戰(zhàn)

1.定義 BMW 抽象類
abstract?class?BMW?{
??abstract?run():?void;
}
2.創(chuàng)建 BMW730 類(BMW 730 Model)
class?BMW730?extends?BMW?{
??run():?void?{
????console.log("BMW730?發(fā)動(dòng)咯");
??}
}
3.創(chuàng)建 BMW840 類(BMW 840 Model)
class?BMW840?extends?BMW?{
??run():?void?{
????console.log("BMW840?發(fā)動(dòng)咯");
??}
}
4.定義 BMWFactory 抽象工廠
abstract?class?BMWFactory?{
??abstract?produce730BMW():?BMW730;
??abstract?produce840BMW():?BMW840;
}
5.創(chuàng)建 ConcreteBMWFactory 類
class?ConcreteBMWFactory?extends?BMWFactory?{
??produce730BMW():?BMW730?{
????return?new?BMW730();
??}
??produce840BMW():?BMW840?{
????return?new?BMW840();
??}
}
6.生產(chǎn)并發(fā)動(dòng) BMW730 和 BMW840
const?bmwFactory?=?new?ConcreteBMWFactory();
const?bmw730?=?bmwFactory.produce730BMW();
const?bmw840?=?bmwFactory.produce840BMW();
bmw730.run();
bmw840.run();
以上代碼運(yùn)行后的輸出結(jié)果為:
[LOG]:?BMW730?發(fā)動(dòng)咯?
[LOG]:?BMW840?發(fā)動(dòng)咯
通過觀察以上的輸出結(jié)果,我們可以知道我們的 ConcreteBMWFactory 已經(jīng)可以正常工作了。在 ConcreteBMWFactory 類中,阿寶哥定義了 produce730BMW() 和 produce840BMW() 這兩個(gè)方法,它們分別用于生產(chǎn) BMW730 和 BMW840 車子。
看到這里相信有的小伙會(huì)有疑問 —— 抽象工廠模式與工廠方法模式有什么區(qū)別呢?
抽象工廠模式與工廠方法模式最大的區(qū)別在于:工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu),而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu),一個(gè)工廠等級(jí)結(jié)構(gòu)可以負(fù)責(zé)多個(gè)不同產(chǎn)品等級(jí)結(jié)構(gòu)中的產(chǎn)品對(duì)象的創(chuàng)建。 當(dāng)一個(gè)工廠等級(jí)結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級(jí)結(jié)構(gòu)的一個(gè)產(chǎn)品族中的所有對(duì)象時(shí),抽象工廠模式比工廠方法模式更為簡單、有效率。
了解完抽象工廠模式與工廠方法模式的區(qū)別,最后我們來一起看一下抽象工廠模式的優(yōu)缺點(diǎn)。
3.3 抽象工廠優(yōu)缺點(diǎn)
優(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ì)象。這對(duì)一些需要根據(jù)當(dāng)前環(huán)境來決定其行為的軟件系統(tǒng)來說,是一種非常實(shí)用的設(shè)計(jì)模式。
缺點(diǎn)
在添加新的產(chǎn)品對(duì)象時(shí),難以擴(kuò)展抽象工廠來生產(chǎn)新種類的產(chǎn)品,這是因?yàn)樵诔橄蠊S角色中規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合,要支持新種類的產(chǎn)品就意味著要對(duì)該接口進(jìn)行擴(kuò)展,而這將涉及到對(duì)抽象工廠角色及其所有子類的修改,顯然會(huì)帶來較大的不便。
四、總結(jié)
根據(jù)模式是用來完成什么工作來劃分,這種方式可分為 創(chuàng)建型模式、結(jié)構(gòu)型模式和行為型模式 3 種。本文介紹的工廠方法和抽象工廠屬于創(chuàng)建型模式,該模式用于描述 “怎樣創(chuàng)建對(duì)象”,它的主要特點(diǎn)是 “將對(duì)象的創(chuàng)建與使用分離”。
GoF(Gang of Four)中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種創(chuàng)建型模式。其中阿寶哥已經(jīng)介紹過單例、工廠方法和抽象工廠,在后續(xù)的文章中阿寶哥將介紹建造者模式及如何基于建造者設(shè)計(jì)模式實(shí)現(xiàn)以下功能,感興趣的小伙伴記得關(guān)注喲。
let?builder:?QueryBuilder?=?new?QueryBuilder();
let?query:?Query?=?new?Query();
builder.query?=?query;
builder.bool().shouldMatch("lot_number",?307).bool().mustMatch("expiry_date",?"September?2020");
console.log(query.toString());
1995 年,艾瑞克·伽馬(ErichGamma)、理査德·海爾姆(Richard Helm)、拉爾夫·約翰森(Ralph Johnson)、約翰·威利斯迪斯(John Vlissides)等 4 位作者合作出版了《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》(Design Patterns: Elements of Reusable Object-Oriented Software)一書,在本教程中收錄了 23 個(gè)設(shè)計(jì)模式,這是設(shè)計(jì)模式領(lǐng)域里程碑的事件,導(dǎo)致了軟件設(shè)計(jì)模式的突破。這 4 位作者在軟件開發(fā)領(lǐng)域里也以他們的 “四人組”(Gang of Four,GoF)匿名著稱。
五、參考資源
簡單工廠模式(SimpleFactoryPattern) 工廠方法模式(Factory Method) 抽象工廠模式(Abstract Factory)- 最易懂的設(shè)計(jì)模式解析 GoF 的 23 種設(shè)計(jì)模式的分類和功能
TypeScript 設(shè)計(jì)模式之適配器模式
聚焦全棧,專注分享 TypeScript、Web API、Deno 等技術(shù)干貨。
