前言
今天開始學習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)代工一家品牌的球類,注定會被其它工廠淘汰厨姚,因此衅澈,它不能過于依賴一家品牌的訂單,需要接受各種品牌的訂單遣蚀,才能形成自己的競爭力矾麻。例如: adidas
、nick
芭梯、lining
等等险耀。不同的品牌使用的材質(zhì)、做工可能是不一樣的玖喘,像上面這三個品牌就是對應(yīng)的類簇甩牺。類簇一般用父類定義,并在父類中定義一些抽象方法累奈,再通過抽象工廠讓子類繼承父類贬派。因此,抽象工廠其實就是一個實現(xiàn)子類繼承父類的方法澎媒。
抽象類是一種聲明但不能使用的類搞乏,JavaScript
中abstract
還是保留字,不能像傳統(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è)計模式》張容銘