面試官:說說你對工廠模式的理解?應用場景?
一、是什么
工廠模式是用來創(chuàng)建對象的一種最常用的設(shè)計模式,不暴露創(chuàng)建對象的具體邏輯,而是將將邏輯封裝在一個函數(shù)中,那么這個函數(shù)就可以被視為一個工廠
其就像工廠一樣重復的產(chǎn)生類似的產(chǎn)品,工廠模式只需要我們傳入正確的參數(shù),就能生產(chǎn)類似的產(chǎn)品
舉個例子:
- 編程中,在一個 A 類中通過 new 的方式實例化了類 B,那么 A 類和 B 類之間就存在關(guān)聯(lián)(耦合)
- 后期因為需要修改了 B 類的代碼和使用方式,比如構(gòu)造函數(shù)中傳入?yún)?shù),那么 A 類也要跟著修改,一個類的依賴可能影響不大,但若有多個類依賴了 B 類,那么這個工作量將會相當?shù)拇?,容易出現(xiàn)修改錯誤,也會產(chǎn)生很多的重復代碼,這無疑是件非常痛苦的事;
- 這種情況下,就需要將創(chuàng)建實例的工作從調(diào)用方(A類)中分離,與調(diào)用方「解耦」,也就是使用工廠方法創(chuàng)建實例的工作封裝起來(「減少代碼重復」),由工廠管理對象的創(chuàng)建邏輯,調(diào)用方不需要知道具體的創(chuàng)建過程,只管使用,「而降低調(diào)用者因為創(chuàng)建邏輯導致的錯誤」;
二、實現(xiàn)
工廠模式根據(jù)抽象程度的不同可以分為:
- 簡單工廠模式(Simple Factory)
- 工廠方法模式(Factory Method)
- 抽象工廠模式(Abstract Factory)
簡單工廠模式
簡單工廠模式也叫靜態(tài)工廠模式,用一個工廠對象創(chuàng)建同一類對象類的實例
假設(shè)我們要開發(fā)一個公司崗位及其工作內(nèi)容的錄入信息,不同崗位的工作內(nèi)容不一致
代碼如下:
- function Factory(career) {
- function User(career, work) {
- this.career = career
- this.work = work
- }
- let work
- switch(career) {
- case 'coder':
- work = ['寫代碼', '修Bug']
- return new User(career, work)
- break
- case 'hr':
- work = ['招聘', '員工信息管理']
- return new User(career, work)
- break
- case 'driver':
- work = ['開車']
- return new User(career, work)
- break
- case 'boss':
- work = ['喝茶', '開會', '審批文件']
- return new User(career, work)
- break
- }
- }
- let coder = new Factory('coder')
- console.log(coder)
- let boss = new Factory('boss')
- console.log(boss)
Factory就是一個簡單工廠。當我們調(diào)用工廠函數(shù)時,只需要傳遞name、age、career就可以獲取到包含用戶工作內(nèi)容的實例對象
工廠方法模式
工廠方法模式跟簡單工廠模式差不多,但是把具體的產(chǎn)品放到了工廠函數(shù)的prototype中
這樣一來,擴展產(chǎn)品種類就不必修改工廠函數(shù)了,變成抽象類,也可以隨時重寫某種具體的產(chǎn)品
也就是相當于工廠總部不生產(chǎn)產(chǎn)品了,交給下轄分工廠進行生產(chǎn);但是進入工廠之前,需要有個判斷來驗證你要生產(chǎn)的東西是否是屬于我們工廠所生產(chǎn)范圍,如果是,就丟給下轄工廠來進行生產(chǎn)
如下代碼:
- // 工廠方法
- function Factory(career){
- if(this instanceof Factory){
- var a = new this[career]();
- return a;
- }else{
- return new Factory(career);
- }
- }
- // 工廠方法函數(shù)的原型中設(shè)置所有對象的構(gòu)造函數(shù)
- Factory.prototype={
- 'coder': function(){
- this.careerName = '程序員'
- this.work = ['寫代碼', '修Bug']
- },
- 'hr': function(){
- this.careerName = 'HR'
- this.work = ['招聘', '員工信息管理']
- },
- 'driver': function () {
- this.careerName = '司機'
- this.work = ['開車']
- },
- 'boss': function(){
- this.careerName = '老板'
- this.work = ['喝茶', '開會', '審批文件']
- }
- }
- let coder = new Factory('coder')
- console.log(coder)
- let hr = new Factory('hr')
- console.log(hr)
工廠方法關(guān)鍵核心代碼是工廠里面的判斷this是否屬于工廠,也就是做了分支判斷,這個工廠只做我能做的產(chǎn)品
抽象工廠模式
上述簡單工廠模式和工廠方法模式都是直接生成實例,但是抽象工廠模式不同,抽象工廠模式并不直接生成實例, 而是用于對產(chǎn)品類簇的創(chuàng)建
通俗點來講就是:簡單工廠和工廠方法模式的工作是生產(chǎn)產(chǎn)品,那么抽象工廠模式的工作就是生產(chǎn)工廠的
由于JavaScript中并沒有抽象類的概念,只能模擬,可以分成四部分:
- 用于創(chuàng)建抽象類的函數(shù)
- 抽象類
- 具體類
- 實例化具體類
上面的例子中有coder、hr、boss、driver四種崗位,其中coder可能使用不同的開發(fā)語言進行開發(fā),比如JavaScript、Java等等。那么這兩種語言就是對應的類簇
示例代碼如下:
- let CareerAbstractFactory = function(subType, superType) {
- // 判斷抽象工廠中是否有該抽象類
- if (typeof CareerAbstractFactory[superType] === 'function') {
- // 緩存類
- function F() {}
- // 繼承父類屬性和方法
- F.prototype = new CareerAbstractFactory[superType]()
- // 將子類的constructor指向父類
- subType.constructor = subType;
- // 子類原型繼承父類
- subType.prototype = new F()
- } else {
- throw new Error('抽象類不存在')
- }
- }
上面代碼中CareerAbstractFactory就是一個抽象工廠方法,該方法在參數(shù)中傳遞子類和父類,在方法體內(nèi)部實現(xiàn)了子類對父類的繼承
三、應用場景
從上面可看到,簡單簡單工廠的優(yōu)點就是我們只要傳遞正確的參數(shù),就能獲得所需的對象,而不需要關(guān)心其創(chuàng)建的具體細節(jié)
應用場景也容易識別,有構(gòu)造函數(shù)的地方,就應該考慮簡單工廠,但是如果函數(shù)構(gòu)建函數(shù)太多與復雜,會導致工廠函數(shù)變得復雜,所以不適合復雜的情況
抽象工廠模式一般用于嚴格要求以面向?qū)ο笏枷脒M行開發(fā)的超大型項目中,我們一般常規(guī)的開發(fā)的話一般就是簡單工廠和工廠方法模式會用的比較多一些
綜上,工廠模式適用場景如下:
- 如果你不想讓某個子系統(tǒng)與較大的那個對象之間形成強耦合,而是想運行時從許多子系統(tǒng)中進行挑選的話,那么工廠模式是一個理想的選擇
- 將new操作簡單封裝,遇到new的時候就應該考慮是否用工廠模式;
- 需要依賴具體環(huán)境創(chuàng)建不同實例,這些實例都有相同的行為,這時候我們可以使用工廠模式,簡化實現(xiàn)的過程,同時也可以減少每種對象所需的代碼量,有利于消除對象間的耦合,提供更大的靈活性
參考文獻
https://www.runoob.com/design-pattern/factory-pattern.html
https://juejin.cn/post/6844903653774458888
https://zhuanlan.zhihu.com/p/344119981