JavaScript設(shè)計模式——工廠模式

前言

今天開始學習JavaScript設(shè)計模式顽聂,每天學一點肥惭,希望有所收獲。
今天主要學習工廠模式紊搪,包括:簡單工廠模式蜜葱、工廠方法模式、抽象工廠模式耀石。

設(shè)計模式分類

1牵囤、創(chuàng)建型

創(chuàng)建型設(shè)計模式專注于處理對象創(chuàng)建機制 ,以適合給定情況的方式來創(chuàng)建對象滞伟。創(chuàng)建對象的基本方法可能導致項目復雜性增加揭鳞,而這些模式旨在通過控制創(chuàng)建過程來解決這種問題。
* Constructor (構(gòu)造器)
* Factory(工廠)
* Abstract(抽象)
* Prototype(原型)
* Singleton(單例)
* Builder(生成器)

2梆奈、結(jié)構(gòu)型

結(jié)構(gòu)型模式與對象組合有關(guān)野崇,通常可以用于在找出在不同對象之間建立關(guān)系的簡單方法亩钟。這種模式有助于確保在系統(tǒng)某一部分發(fā)生變化時乓梨,系統(tǒng)的整個結(jié)構(gòu)不需要同是改變。同時對于不適合因某一特定目的而改變的系統(tǒng)部分清酥,這種模式也能夠幫助它們完成重組扶镀。
* Decorator(裝飾者)
* Facade(外觀)
* Flyweight(享元)
* Adapter(適配器)
* Proxy(代理)

3、行為

行為模式專注于改善或簡化系統(tǒng)中不同對象之間的通信焰轻。
* Interator(迭代器)
* Mediator(中介者)
* Observer(觀察者)
* Visitor(訪問)

工廠模式 ??

1臭觉、簡單工廠模式

又叫靜態(tài)工廠模式,就是創(chuàng)建對象辱志,并賦予屬性和方法蝠筑。主要用來創(chuàng)建同一類對象。

栗子??:
生產(chǎn)球的工廠有各種球揩懒,我們只需告訴它菱肖,你想要的球的名字,它便會把球生產(chǎn)出來:

let Basketball = () => {
    this.intro = "??籃球盛行于美國";
}
Basketball.prototype = {
    getNumber: function () {
        console.log("每個隊伍需要5名隊員");
    },
    getBallSize: function () {
        console.log("??籃球很大");
    }
}
let Football = () => {
    this.intro = "??足球在世界范圍內(nèi)都很流行";
}
Football.prototype = {
    getNumber: function () {
        console.log("每個隊伍需要11名隊員");
    },
    getBallSize: function () {
        console.log("??足球很大");
    }
}
let Tennis = () => {
    this.intro = "每年有很多網(wǎng)球??比賽";
}
Tennis.prototype = {
    getNumber: function () {
        console.log("每個隊伍需要1名隊員");
    },
    getBallSize: function () {
        console.log("??網(wǎng)球很小");
    }
}

// 體育用品工廠
let SportsFactory = function (name) {
    switch(name) {
        case 'basketball':
            return new Basketball();
        case 'football':
            return new Football();
        case 'tennis':
            return new Tennis();
    }
}

當我們想要足球時旭从,你可以告訴工廠:football稳强,它便會將你想要的給你:

let football = SportsFactory('football');
console.log(football.intro); // ??足球在世界范圍內(nèi)都很流行
football.getMember();   // 每個隊伍需要11名隊員

這個時候,你會發(fā)現(xiàn)和悦,上面的三種球的內(nèi)部結(jié)構(gòu)很相似退疫,為了減少代碼的重復,我們對它進行優(yōu)化:

let SportsFactory = function (name) {
    function Balls(option) {
        this.intro = option.intro;
        this.getNumber = function () {
            console.log(option.number)
        }
        this.getSize = function () {
            console.log(option.size)
        }
    }
    switch(name) {
        case 'basketball':
            return new Balls({
                intro: "??籃球盛行于美國",
                number: "每個隊伍需要5名隊員",
                size: "??籃球很大"
            });
        case 'football':
            return new Balls({
                intro: "??足球在世界范圍內(nèi)都很流行",
                number: "每個隊伍需要11名隊員",
                size: "??足球很大"
            });
        case 'tennis':
            return new Balls({
                intro: "??籃球盛行于美國",
                number: "每個隊伍需要5名隊員",
                size: "??籃球很大"
            });
    }
}

顯而易見鸽素,我們無需了解褒繁,這個工廠是怎么把球給造出來的,只要給到相應(yīng)的參數(shù)馍忽,它便給我們相應(yīng)的球棒坏,簡單又方便燕差。但是,當我們需要更多種類的球時坝冕,如:橄欖球??徒探、羽毛球???、臺球??...balabala...球的種類越多喂窟,工廠如果還是按照這種方式去生產(chǎn)球测暗,它會變得越來越臃腫,變得難以維護磨澡。所以說碗啄,簡單工廠模式,只適用于稳摄,對象較少稚字,對象邏輯簡單的情況。

工廠需要創(chuàng)新厦酬,才能獲得更多的利潤:

2胆描、工廠方法模式

通過對產(chǎn)品類的抽象,使其創(chuàng)建業(yè)務(wù)主要負責用于創(chuàng)建多類產(chǎn)品的實例弃锐。
也就是說袄友,將實際創(chuàng)建對象的工作殿托,放在子類中霹菊,把核心類抽離出來,形成抽象類支竹。

我們可以將工廠方法看作是一個實例化對象的工廠類旋廷。按照工廠方法模式,我們對上面的代碼進行改造礼搁。安全起見饶碘,我們采用安全模式類,而我們將創(chuàng)建的基類放在工廠方法類的原型中即可馒吴。

// 安全模式創(chuàng)建的工廠類
let SportsFactory = function(name) {
    if(this instanceof SportsFactory){
        let s = new this[name]();
        return s
    } else {
        return new SportsFactory(name);
    }
}

// 工廠原型中設(shè)置創(chuàng)建所有類型數(shù)據(jù)對象的基類
SportsFactory.prototype = {
    football : function() {
        this.intro = "??足球在世界范圍內(nèi)都很流行";
        this.getNumber = function () {
            console.log("每個隊伍需要11名隊員");
        },
        this.getBallSize = function () {
            console.log("??足球很大");
        }
    },
    basketball : function() {
        this.intro = "??籃球盛行于美國";
        this.getNumber = function () {
            console.log("每個隊伍需要5名隊員");
        },
        this.getBallSize = function () {
            console.log("??籃球很大");
        }
    },
    tennis: function() {
        this.intro = "每年有很多網(wǎng)球??比賽";
        this.getNumber = function () {
            console.log("每個隊伍需要1名隊員");
        },
        this.getBallSize = function () {
            console.log("??網(wǎng)球很小");
        }
    }
}

let football = SportsFactory('football')
football.getBallSize()  // ??足球很大

這樣以后添加其他類時扎运, 只要寫在SportsFactory這個工廠類的原型里就可以了。
通過工廠方法模式饮戳,我們可以輕松的創(chuàng)建多個類的實例對象豪治,這樣工廠方法對象在創(chuàng)建對象的方式,避免了使用者與對象類之間的耦合扯罐,用戶不必關(guān)心創(chuàng)建該對象的具體類负拟,只需調(diào)用工廠方法即可。

3歹河、抽象工廠模式

通過對類的工廠抽象使其業(yè)務(wù)對于產(chǎn)品類簇的創(chuàng)建掩浙,而不負責某一類產(chǎn)品的實例花吟。

工廠如果只生產(chǎn)代工一家品牌的球類,注定會被其它工廠淘汰厨姚,因此衅澈,它不能過于依賴一家品牌的訂單,需要接受各種品牌的訂單遣蚀,才能形成自己的競爭力矾麻。例如: adidasnick芭梯、lining等等险耀。不同的品牌使用的材質(zhì)、做工可能是不一樣的玖喘,像上面這三個品牌就是對應(yīng)的類簇甩牺。類簇一般用父類定義,并在父類中定義一些抽象方法累奈,再通過抽象工廠讓子類繼承父類贬派。因此,抽象工廠其實就是一個實現(xiàn)子類繼承父類的方法澎媒。

抽象類是一種聲明但不能使用的類搞乏,JavaScriptabstract還是保留字,不能像傳統(tǒng)面向?qū)ο笳Z言那樣輕松的創(chuàng)建抽象類戒努。不過请敦,我們可以手動地拋出錯誤來模擬抽象類。

// 抽象工廠方法
let SportsFactory = function() {}
SportsFactory.prototype = {
    getNumber: function () {
        return new Error('抽象方法不可調(diào)用');
    }
}

以上代碼就是一個抽象類储玫,啥也做不了侍筛。但是在繼承上卻很有用,如果在子類中沒有重寫這些方法撒穷,那當子類調(diào)用改方法的時候匣椰,便會報錯。這對于子類忘記重寫這些方法時端礼,父類的提示顯得非常友好禽笑。

抽象類中定義的方法只是顯性地定義一些功能,但沒有具體的實現(xiàn)蛤奥,而一個對象是要具有一套完整的功能的佳镜,所以,用抽象類創(chuàng)建的對象當時也是“抽象的”喻括,因此邀杏,我們不能用它來創(chuàng)建一個真實的對象。

也就是說,工廠雖然拿到了訂單望蜡,但是還沒有可以生產(chǎn)不同品牌產(chǎn)品的設(shè)備唤崭,下面我們便來實現(xiàn)這些設(shè)備:

let SportsFactory = function (subType, superType) {
    // 判斷抽象工廠中是否有該抽象類
    if (typeof SportsFactory[superType] === 'function') {
        // 緩存
        function F(){};
        // 繼承父類屬性和方法
        F.prototype = new SportsFactory[superType] ();
        // 將子類 constructor 指向子類
        subType.constructor = subType;
        // 子類原型繼承 “父類”
        subType.prototype = new F ();
    } else { 
        // 不存在該抽象類拋出錯誤
        throw new Error('未創(chuàng)建該抽象類');
    }
}

//阿迪達斯抽象類
SportsFactory.AdidasBall = function() {
  this.type = 'adidas';
}
SportsFactory.AdidasBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能調(diào)用');
  }
}

//耐克抽象類
SportsFactory.NickBall = function() {
  this.type = 'nick';
}
SportsFactory.NickBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能調(diào)用');
  }
}

//李寧抽象類
SportsFactory.LiNingBall = function() {
  this.type = 'lining';
}
SportsFactory.LiNingBall.prototype = {
  getNumber: function() {
    return new Error('抽象方法不能調(diào)用');
  }
}

SportsFactory就是一個抽象工廠方法,該方法在參數(shù)中傳遞子類和父類脖律,在方法體內(nèi)部谢肾,通過寄生式繼承,實現(xiàn)了子類對父類的繼承小泉。對抽象工廠方法添加抽象類的方法我們是通過點語法進行添加的芦疏。

好了,設(shè)備買回來了微姊,我們便開始生產(chǎn)你想要的產(chǎn)品啦:

function BallsOfAdidas(option) {
    this.intro = "??足球在世界范圍內(nèi)都很流行";
}

//抽象工廠實現(xiàn)WechatUser類的繼承
SportsFactory(BallsOfAdidas, 'AdidasBall');
//子類中重寫抽象方法
BallsOfAdidas.prototype.getNumber = function() {
  console.log("每個隊伍需要11名隊員");
}

BallsOfAdidas通過SportsFactory工廠類繼承了AdidasBall酸茴,并且重寫了父類AdidasBall中的getNumber方法。

生產(chǎn)完成后兢交,我們便可以去買自己想要的品牌的運動器材了:

let adidasFootball = new BallsOfAdidas();
console.log(adidasFootball.type);   //adidas
adidasFootball.getNumber();    //每個隊伍需要11名隊員

抽象工廠模式是設(shè)計模式中最抽象的一種薪捍,也是創(chuàng)建模式中唯一一種抽象化創(chuàng)建模式。我們可以看到配喳,抽象工廠創(chuàng)建出一個個類簇酪穿,固定了類的結(jié)構(gòu),它不直接創(chuàng)建實例晴裹,而是通過類的繼承進行類簇的管理被济。

總結(jié)

1、簡單工廠模式中涧团,工廠Factory類集中了所有產(chǎn)品創(chuàng)建的邏輯只磷,一旦要拓展新產(chǎn)品時,就不得不修改工廠類少欺,這就違反了開閉原則(對拓展開放喳瓣,對修改封閉)馋贤,并且會造成工廠的邏輯過于復雜赞别。

2鳞骤、工廠方法模式中凤粗,在新增一個新產(chǎn)品時,就要新增一個具體工廠和一個具體產(chǎn)品類槐瑞,這樣程序的拓展性就有了提高犹芹,符合了開閉原則崎页,避免了簡單工廠模式的缺點,但是呢腰埂,新增產(chǎn)品時需要新增兩個類飒焦,會增加代碼量,可謂是有舍有得,具體如何要結(jié)合具體情況來使用牺荠。

3翁巍、抽象工廠模式是所有工廠模式的一般形式,當抽象工廠模式退化到只有一個產(chǎn)品等級結(jié)構(gòu)時休雌,就變成了工廠方法模式灶壶。當工廠方法模式的工廠類只有一個時,且工廠方法為靜態(tài)方法時杈曲,則又變成了簡單工廠模式驰凛。與工廠方法模式相似,抽象工廠模式隔離了具體類的生成担扑,讓客戶端不清楚具體什么樣的對象被創(chuàng)建恰响。

參考

  • 《JavaScript設(shè)計模式》張容銘
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涌献,隨后出現(xiàn)的幾起案子渔隶,更是在濱河造成了極大的恐慌,老刑警劉巖洁奈,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件间唉,死亡現(xiàn)場離奇詭異,居然都是意外死亡利术,警方通過查閱死者的電腦和手機呈野,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來印叁,“玉大人被冒,你說我怎么就攤上這事÷滞桑” “怎么了昨悼?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長跃洛。 經(jīng)常有香客問我率触,道長,這世上最難降的妖魔是什么汇竭? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任葱蝗,我火速辦了婚禮,結(jié)果婚禮上细燎,老公的妹妹穿的比我還像新娘两曼。我一直安慰自己,他們只是感情好玻驻,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布悼凑。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪户辫。 梳的紋絲不亂的頭發(fā)上益老,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音寸莫,去河邊找鬼捺萌。 笑死,一個胖子當著我的面吹牛膘茎,可吹牛的內(nèi)容都是我干的桃纯。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼披坏,長吁一口氣:“原來是場噩夢啊……” “哼态坦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起棒拂,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤伞梯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帚屉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谜诫,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年攻旦,在試婚紗的時候發(fā)現(xiàn)自己被綠了喻旷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡牢屋,死狀恐怖且预,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烙无,我是刑警寧澤锋谐,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站截酷,受9級特大地震影響涮拗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜合搅,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一多搀、第九天 我趴在偏房一處隱蔽的房頂上張望歧蕉。 院中可真熱鬧灾部,春花似錦、人聲如沸惯退。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锁蠕,卻和暖如春夷野,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荣倾。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工悯搔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舌仍。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓妒貌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铸豁。 傳聞我的和親對象是個殘疾皇子灌曙,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容