在JavaScript中并沒有類這種概念,JS中的函數(shù)屬于一等對象,在JS中定義一個對象非常簡單(var obj = {}),而基" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > JavaScript中常見的十五種設(shè)計模式(上)

JavaScript中常見的十五種設(shè)計模式(上)

時間:2023-09-04 05:42:01 | 來源:網(wǎng)站運營

時間:2023-09-04 05:42:01 來源:網(wǎng)站運營

JavaScript中常見的十五種設(shè)計模式(上):在程序設(shè)計中有很多實用的設(shè)計模式,而其中大部分語言的實現(xiàn)都是基于“類”。

在JavaScript中并沒有類這種概念,JS中的函數(shù)屬于一等對象,在JS中定義一個對象非常簡單(var obj = {}),而基于JS中閉包與弱類型等特性,在實現(xiàn)一些設(shè)計模式的方式上與眾不同。

本文基于《JavaScript設(shè)計模式與開發(fā)實踐》一書,用一些例子總結(jié)一下JS常見的設(shè)計模式與實現(xiàn)方法。文章略長,篇幅有限分成上下兩篇,自備瓜子板凳~

本文為上篇,前往下篇

設(shè)計原則

單一職責(zé)原則(SRP)

一個對象或方法只做一件事情。如果一個方法承擔了過多的職責(zé),那么在需求的變遷過程中,需要改寫這個方法的可能性就越大。

應(yīng)該把對象或方法劃分成較小的粒度

最少知識原則(LKP)

一個軟件實體應(yīng)當 盡可能少地與其他實體發(fā)生相互作用

應(yīng)當盡量減少對象之間的交互。如果兩個對象之間不必彼此直接通信,那么這兩個對象就不要發(fā)生直接的 相互聯(lián)系,可以轉(zhuǎn)交給第三方進行處理

開放-封閉原則(OCP)

軟件實體(類、模塊、函數(shù))等應(yīng)該是可以 擴展的,但是不可修改

當需要改變一個程序的功能或者給這個程序增加新功能的時候,可以使用增加代碼的方式,盡量避免改動程序的源代碼,防止影響原系統(tǒng)的穩(wěn)定

什么是設(shè)計模式

作者的這個說明解釋得挺好

假設(shè)有一個空房間,我們要日復(fù)一日地往里 面放一些東西。最簡單的辦法當然是把這些東西 直接扔進去,但是時間久了,就會發(fā)現(xiàn)很難從這 個房子里找到自己想要的東西,要調(diào)整某幾樣?xùn)| 西的位置也不容易。所以在房間里做一些柜子也 許是個更好的選擇,雖然柜子會增加我們的成 本,但它可以在維護階段為我們帶來好處。使用 這些柜子存放東西的規(guī)則,或許就是一種模式
學(xué)習(xí)設(shè)計模式,有助于寫出可復(fù)用和可維護性高的程序

設(shè)計模式的原則是“找出 程序中變化的地方,并將變化封裝起來”,它的關(guān)鍵是意圖,而不是結(jié)構(gòu)。

不過要注意,使用不當?shù)脑?,可能會事倍功半?br>

一、單例模式

1. 定義

保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

2. 核心

確保只有一個實例,并提供全局訪問

3. 實現(xiàn)

假設(shè)要設(shè)置一個管理員,多次調(diào)用也僅設(shè)置一次,我們可以使用閉包緩存一個內(nèi)部變量來實現(xiàn)這個單例

function SetManager(name) { this.manager = name;}SetManager.prototype.getName = function() { console.log(this.manager);};var SingletonSetManager = (function() { var manager = null; return function(name) { if (!manager) { manager = new SetManager(name); } return manager; } })();SingletonSetManager('a').getName(); // aSingletonSetManager('b').getName(); // aSingletonSetManager('c').getName(); // a這是比較簡單的做法,但是假如我們還要設(shè)置一個HR呢?就得復(fù)制一遍代碼了

所以,可以改寫單例內(nèi)部,實現(xiàn)地更通用一些

// 提取出通用的單例function getSingleton(fn) { var instance = null; return function() { if (!instance) { instance = fn.apply(this, arguments); } return instance; }}再進行調(diào)用,結(jié)果還是一樣

// 獲取單例var managerSingleton = getSingleton(function(name) { var manager = new SetManager(name); return manager;});managerSingleton('a').getName(); // amanagerSingleton('b').getName(); // amanagerSingleton('c').getName(); // a這時,我們添加HR時,就不需要更改獲取單例內(nèi)部的實現(xiàn)了,僅需要實現(xiàn)添加HR所需要做的,再調(diào)用即可

function SetHr(name) { this.hr = name;}SetHr.prototype.getName = function() { console.log(this.hr);};var hrSingleton = getSingleton(function(name) { var hr = new SetHr(name); return hr;});hrSingleton('aa').getName(); // aahrSingleton('bb').getName(); // aahrSingleton('cc').getName(); // aa或者,僅想要創(chuàng)建一個div層,不需要將對象實例化,直接調(diào)用函數(shù)

結(jié)果為頁面中僅有第一個創(chuàng)建的div

function createPopup(html) { var div = document.createElement('div'); div.innerHTML = html; document.body.append(div); return div;}var popupSingleton = getSingleton(function() { var div = createPopup.apply(this, arguments); return div;});console.log( popupSingleton('aaa').innerHTML, popupSingleton('bbb').innerHTML, popupSingleton('bbb').innerHTML); // aaa aaa aaa

二、策略模式

1. 定義

定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。

2. 核心

將算法的使用和算法的實現(xiàn)分離開來。

一個基于策略模式的程序至少由兩部分組成:

第一個部分是一組策略類,策略類封裝了具體的算法,并負責(zé)具體的計算過程。

第二個部分是環(huán)境類Context,Context接受客戶的請求,隨后把請求委托給某一個策略類。要做到這點,說明Context 中要維持對某個策略對象的引用

3. 實現(xiàn)

策略模式可以用于組合一系列算法,也可用于組合一系列業(yè)務(wù)規(guī)則

假設(shè)需要通過成績等級來計算學(xué)生的最終得分,每個成績等級有對應(yīng)的加權(quán)值。我們可以利用對象字面量的形式直接定義這個組策略

// 加權(quán)映射關(guān)系var levelMap = { S: 10, A: 8, B: 6, C: 4};// 組策略var scoreLevel = { basicScore: 80, S: function() { return this.basicScore + levelMap['S']; }, A: function() { return this.basicScore + levelMap['A']; }, B: function() { return this.basicScore + levelMap['B']; }, C: function() { return this.basicScore + levelMap['C']; }}// 調(diào)用function getScore(level) { return scoreLevel[level] ? scoreLevel[level]() : 0;}console.log( getScore('S'), getScore('A'), getScore('B'), getScore('C'), getScore('D')); // 90 88 86 84 0在組合業(yè)務(wù)規(guī)則方面,比較經(jīng)典的是表單的驗證方法。這里列出比較關(guān)鍵的部分

// 錯誤提示var errorMsgs = { default: '輸入數(shù)據(jù)格式不正確', minLength: '輸入數(shù)據(jù)長度不足', isNumber: '請輸入數(shù)字', required: '內(nèi)容不為空'};// 規(guī)則集var rules = { minLength: function(value, length, errorMsg) { if (value.length < length) { return errorMsg || errorMsgs['minLength'] } }, isNumber: function(value, errorMsg) { if (!//d+/.test(value)) { return errorMsg || errorMsgs['isNumber']; } }, required: function(value, errorMsg) { if (value === '') { return errorMsg || errorMsgs['required']; } }};// 校驗器function Validator() { this.items = [];};Validator.prototype = { constructor: Validator, // 添加校驗規(guī)則 add: function(value, rule, errorMsg) { var arg = [value]; if (rule.indexOf('minLength') !== -1) { var temp = rule.split(':'); arg.push(temp[1]); rule = temp[0]; } arg.push(errorMsg); this.items.push(function() { // 進行校驗 return rules[rule].apply(this, arg); }); }, // 開始校驗 start: function() { for (var i = 0; i < this.items.length; ++i) { var ret = this.items[i](); if (ret) { console.log(ret); // return ret; } } }};// 測試數(shù)據(jù)function testTel(val) { return val;}var validate = new Validator();validate.add(testTel('ccc'), 'isNumber', '只能為數(shù)字'); // 只能為數(shù)字validate.add(testTel(''), 'required'); // 內(nèi)容不為空validate.add(testTel('123'), 'minLength:5', '最少5位'); // 最少5位validate.add(testTel('12345'), 'minLength:5', '最少5位');var ret = validate.start();console.log(ret);4. 優(yōu)缺點

優(yōu)點

可以有效地避免多重條件語句,將一系列方法封裝起來也更直觀,利于維護

缺點

往往策略集會比較多,我們需要事先就了解定義好所有的情況

三、代理模式

1. 定義

為一個對象提供一個代用品或占位符,以便控制對它的訪問

2. 核心

當客戶不方便直接訪問一個 對象或者不滿足需要的時候,提供一個替身對象 來控制對這個對象的訪問,客戶實際上訪問的是 替身對象。

替身對象對請求做出一些處理之后, 再把請求轉(zhuǎn)交給本體對象

代理和本體的接口具有一致性,本體定義了關(guān)鍵功能,而代理是提供或拒絕對它的訪問,或者在訪問本體之前做一 些額外的事情

3. 實現(xiàn)

代理模式主要有三種:保護代理、虛擬代理、緩存代理

保護代理主要實現(xiàn)了訪問主體的限制行為,以過濾字符作為簡單的例子

// 主體,發(fā)送消息function sendMsg(msg) { console.log(msg);}// 代理,對消息進行過濾function proxySendMsg(msg) { // 無消息則直接返回 if (typeof msg === 'undefined') { console.log('deny'); return; } // 有消息則進行過濾 msg = ('' + msg).replace(/泥/s*煤/g, ''); sendMsg(msg);}sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀proxySendMsg('泥煤呀泥 煤'); // 呀proxySendMsg(); // deny它的意圖很明顯,在訪問主體之前進行控制,沒有消息的時候直接在代理中返回了,拒絕訪問主體,這數(shù)據(jù)保護代理的形式

有消息的時候?qū)γ舾凶址M行了處理,這屬于虛擬代理的模式

虛擬代理在控制對主體的訪問時,加入了一些額外的操作

在滾動事件觸發(fā)的時候,也許不需要頻繁觸發(fā),我們可以引入函數(shù)節(jié)流,這是一種虛擬代理的實現(xiàn)

// 函數(shù)防抖,頻繁操作中不處理,直到操作完成之后(再過 delay 的時間)才一次性處理function debounce(fn, delay) { delay = delay || 200; var timer = null; return function() { var arg = arguments; // 每次操作時,清除上次的定時器 clearTimeout(timer); timer = null; // 定義新的定時器,一段時間后進行操作 timer = setTimeout(function() { fn.apply(this, arg); }, delay); }};var count = 0;// 主體function scrollHandle(e) { console.log(e.type, ++count); // scroll}// 代理var proxyScrollHandle = (function() { return debounce(scrollHandle, 500);})();window.onscroll = proxyScrollHandle;緩存代理可以為一些開銷大的運算結(jié)果提供暫時的緩存,提升效率

來個栗子,緩存加法操作

// 主體function add() { var arg = [].slice.call(arguments); return arg.reduce(function(a, b) { return a + b; });}// 代理var proxyAdd = (function() { var cache = []; return function() { var arg = [].slice.call(arguments).join(','); // 如果有,則直接從緩存返回 if (cache[arg]) { return cache[arg]; } else { var ret = add.apply(this, arguments); return ret; } };})();console.log( add(1, 2, 3, 4), add(1, 2, 3, 4), proxyAdd(10, 20, 30, 40), proxyAdd(10, 20, 30, 40)); // 10 10 100 100

四、迭代器模式

1. 定義

迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內(nèi)部表示。

2. 核心

在使用迭代器模式之后,即使不關(guān)心對象的內(nèi)部構(gòu)造,也可以按順序訪問其中的每個元素

3. 實現(xiàn)

JS中數(shù)組的map forEach 已經(jīng)內(nèi)置了迭代器

[1, 2, 3].forEach(function(item, index, arr) { console.log(item, index, arr);});不過對于對象的遍歷,往往不能與數(shù)組一樣使用同一的遍歷代碼

我們可以封裝一下

function each(obj, cb) { var value; if (Array.isArray(obj)) { for (var i = 0; i < obj.length; ++i) { value = cb.call(obj[i], i, obj[i]); if (value === false) { break; } } } else { for (var i in obj) { value = cb.call(obj[i], i, obj[i]); if (value === false) { break; } } }}each([1, 2, 3], function(index, value) { console.log(index, value);});each({a: 1, b: 2}, function(index, value) { console.log(index, value);});// 0 1// 1 2// 2 3// a 1// b 2再來看一個例子,強行地使用迭代器,來了解一下迭代器也可以替換頻繁的條件語句

雖然例子不太好,但在其他負責(zé)的分支判斷情況下,也是值得考慮的

function getManager() { var year = new Date().getFullYear(); if (year <= 2000) { console.log('A'); } else if (year >= 2100) { console.log('C'); } else { console.log('B'); }}getManager(); // B將每個條件語句拆分出邏輯函數(shù),放入迭代器中迭代

function year2000() { var year = new Date().getFullYear(); if (year <= 2000) { console.log('A'); } return false;}function year2100() { var year = new Date().getFullYear(); if (year >= 2100) { console.log('C'); } return false;}function year() { var year = new Date().getFullYear(); if (year > 2000 && year < 2100) { console.log('B'); } return false;}function iteratorYear() { for (var i = 0; i < arguments.length; ++i) { var ret = arguments[i](); if (ret !== false) { return ret; } }}var manager = iteratorYear(year2000, year2100, year); // B

五、發(fā)布-訂閱模式

1. 定義

也稱作觀察者模式,定義了對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā) 生改變時,所有依賴于它的對象都將得到通知

2. 核心

取代對象之間硬編碼的通知機制,一個對象不用再顯式地調(diào)用另外一個對象的某個接口。

與傳統(tǒng)的發(fā)布-訂閱模式實現(xiàn)方式(將訂閱者自身當成引用傳入發(fā)布者)不同,在JS中通常使用注冊回調(diào)函數(shù)的形式來訂閱

3. 實現(xiàn)

JS中的事件就是經(jīng)典的發(fā)布-訂閱模式的實現(xiàn)

// 訂閱document.body.addEventListener('click', function() { console.log('click1');}, false);document.body.addEventListener('click', function() { console.log('click2');}, false);// 發(fā)布document.body.click(); // click1 click2自己實現(xiàn)一下

小A在公司C完成了筆試及面試,小B也在公司C完成了筆試。他們焦急地等待結(jié)果,每隔半天就電話詢問公司C,導(dǎo)致公司C很不耐煩。

一種解決辦法是 AB直接把聯(lián)系方式留給C,有結(jié)果的話C自然會通知AB

這里的“詢問”屬于顯示調(diào)用,“留給”屬于訂閱,“通知”屬于發(fā)布

// 觀察者var observer = { // 訂閱集合 subscribes: [], // 訂閱 subscribe: function(type, fn) { if (!this.subscribes[type]) { this.subscribes[type] = []; } // 收集訂閱者的處理 typeof fn === 'function' && this.subscribes[type].push(fn); }, // 發(fā)布 可能會攜帶一些信息發(fā)布出去 publish: function() { var type = [].shift.call(arguments), fns = this.subscribes[type]; // 不存在的訂閱類型,以及訂閱時未傳入處理回調(diào)的 if (!fns || !fns.length) { return; } // 挨個處理調(diào)用 for (var i = 0; i < fns.length; ++i) { fns[i].apply(this, arguments); } }, // 刪除訂閱 remove: function(type, fn) { // 刪除全部 if (typeof type === 'undefined') { this.subscribes = []; return; } var fns = this.subscribes[type]; // 不存在的訂閱類型,以及訂閱時未傳入處理回調(diào)的 if (!fns || !fns.length) { return; } if (typeof fn === 'undefined') { fns.length = 0; return; } // 挨個處理刪除 for (var i = 0; i < fns.length; ++i) { if (fns[i] === fn) { fns.splice(i, 1); } } }};// 訂閱崗位列表function jobListForA(jobs) { console.log('A', jobs);}function jobListForB(jobs) { console.log('B', jobs);}// A訂閱了筆試成績observer.subscribe('job', jobListForA);// B訂閱了筆試成績observer.subscribe('job', jobListForB);// A訂閱了筆試成績observer.subscribe('examinationA', function(score) { console.log(score);});// B訂閱了筆試成績observer.subscribe('examinationB', function(score) { console.log(score);});// A訂閱了面試結(jié)果observer.subscribe('interviewA', function(result) { console.log(result);});observer.publish('examinationA', 100); // 100observer.publish('examinationB', 80); // 80observer.publish('interviewA', '備用'); // 備用observer.publish('job', ['前端', '后端', '測試']); // 輸出A和B的崗位// B取消訂閱了筆試成績observer.remove('examinationB');// A都取消訂閱了崗位observer.remove('job', jobListForA);observer.publish('examinationB', 80); // 沒有可匹配的訂閱,無輸出observer.publish('job', ['前端', '后端', '測試']); // 輸出B的崗位4. 優(yōu)缺點

優(yōu)點

一為時間上的解耦,二為對象之間的解耦??梢杂迷诋惒骄幊讨信cMV*框架中

缺點

創(chuàng)建訂閱者本身要消耗一定的時間和內(nèi)存,訂閱的處理函數(shù)不一定會被執(zhí)行,駐留內(nèi)存有性能開銷

弱化了對象之間的聯(lián)系,復(fù)雜的情況下可能會導(dǎo)致程序難以跟蹤維護和理解

六、命令模式

1. 定義

用一種松耦合的方式來設(shè)計程序,使得請求發(fā)送者和請求接收者能夠消除彼此之間的耦合關(guān)系

命令(command)指的是一個執(zhí)行某些特定事情的指令

2. 核心

命令中帶有execute執(zhí)行、undo撤銷、redo重做等相關(guān)命令方法,建議顯示地指示這些方法名

3. 實現(xiàn)

簡單的命令模式實現(xiàn)可以直接使用對象字面量的形式定義一個命令

var incrementCommand = { execute: function() { // something }};不過接下來的例子是一個自增命令,提供執(zhí)行、撤銷、重做功能

采用對象創(chuàng)建處理的方式,定義這個自增

// 自增function IncrementCommand() { // 當前值 this.val = 0; // 命令棧 this.stack = []; // 棧指針位置 this.stackPosition = -1;};IncrementCommand.prototype = { constructor: IncrementCommand, // 執(zhí)行 execute: function() { this._clearRedo(); // 定義執(zhí)行的處理 var command = function() { this.val += 2; }.bind(this); // 執(zhí)行并緩存起來 command(); this.stack.push(command); this.stackPosition++; this.getValue(); }, canUndo: function() { return this.stackPosition >= 0; }, canRedo: function() { return this.stackPosition < this.stack.length - 1; }, // 撤銷 undo: function() { if (!this.canUndo()) { return; } this.stackPosition--; // 命令的撤銷,與執(zhí)行的處理相反 var command = function() { this.val -= 2; }.bind(this); // 撤銷后不需要緩存 command(); this.getValue(); }, // 重做 redo: function() { if (!this.canRedo()) { return; } // 執(zhí)行棧頂?shù)拿?/span> this.stack[++this.stackPosition](); this.getValue(); }, // 在執(zhí)行時,已經(jīng)撤銷的部分不能再重做 _clearRedo: function() { this.stack = this.stack.slice(0, this.stackPosition + 1); }, // 獲取當前值 getValue: function() { console.log(this.val); }};再實例化進行測試,模擬執(zhí)行、撤銷、重做的操作

var incrementCommand = new IncrementCommand();// 模擬事件觸發(fā),執(zhí)行命令var eventTrigger = { // 某個事件的處理中,直接調(diào)用命令的處理方法 increment: function() { incrementCommand.execute(); }, incrementUndo: function() { incrementCommand.undo(); }, incrementRedo: function() { incrementCommand.redo(); }};eventTrigger['increment'](); // 2eventTrigger['increment'](); // 4eventTrigger['incrementUndo'](); // 2eventTrigger['increment'](); // 4eventTrigger['incrementUndo'](); // 2eventTrigger['incrementUndo'](); // 0eventTrigger['incrementUndo'](); // 無輸出eventTrigger['incrementRedo'](); // 2eventTrigger['incrementRedo'](); // 4eventTrigger['incrementRedo'](); // 無輸出eventTrigger['increment'](); // 6此外,還可以實現(xiàn)簡單的宏命令(一系列命令的集合)

var MacroCommand = { commands: [], add: function(command) { this.commands.push(command); return this; }, remove: function(command) { if (!command) { this.commands = []; return; } for (var i = 0; i < this.commands.length; ++i) { if (this.commands[i] === command) { this.commands.splice(i, 1); } } }, execute: function() { for (var i = 0; i < this.commands.length; ++i) { this.commands[i].execute(); } }};var showTime = { execute: function() { console.log('time'); }};var showName = { execute: function() { console.log('name'); }};var showAge = { execute: function() { console.log('age'); }};MacroCommand.add(showTime).add(showName).add(showAge);MacroCommand.remove(showName);MacroCommand.execute(); // time age

七、組合模式

1. 定義

是用小的子對象來構(gòu)建更大的 對象,而這些小的子對象本身也許是由更小 的“孫對象”構(gòu)成的。

2. 核心

可以用樹形結(jié)構(gòu)來表示這種“部分- 整體”的層次結(jié)構(gòu)。

調(diào)用組合對象 的execute方法,程序會遞歸調(diào)用組合對象 下面的葉對象的execute方法







但要注意的是,組合模式不是父子關(guān)系,它是一種HAS-A(聚合)的關(guān)系,將請求委托給 它所包含的所有葉對象?;谶@種委托,就需要保證組合對象和葉對象擁有相同的 接口

此外,也要保證用一致的方式對待 列表中的每個葉對象,即葉對象屬于同一類,不需要過多特殊的額外操作

3. 實現(xiàn)

使用組合模式來實現(xiàn)掃描文件夾中的文件

// 文件夾 組合對象function Folder(name) { this.name = name; this.parent = null; this.files = [];}Folder.prototype = { constructor: Folder, add: function(file) { file.parent = this; this.files.push(file); return this; }, scan: function() { // 委托給葉對象處理 for (var i = 0; i < this.files.length; ++i) { this.files[i].scan(); } }, remove: function(file) { if (typeof file === 'undefined') { this.files = []; return; } for (var i = 0; i < this.files.length; ++i) { if (this.files[i] === file) { this.files.splice(i, 1); } } }};// 文件 葉對象function File(name) { this.name = name; this.parent = null;}File.prototype = { constructor: File, add: function() { console.log('文件里面不能添加文件'); }, scan: function() { var name = [this.name]; var parent = this.parent; while (parent) { name.unshift(parent.name); parent = parent.parent; } console.log(name.join(' / ')); }};構(gòu)造好組合對象與葉對象的關(guān)系后,實例化,在組合對象中插入組合或葉對象

var web = new Folder('Web');var fe = new Folder('前端');var css = new Folder('CSS');var js = new Folder('js');var rd = new Folder('后端');web.add(fe).add(rd);var file1 = new File('HTML權(quán)威指南.pdf');var file2 = new File('CSS權(quán)威指南.pdf');var file3 = new File('JavaScript權(quán)威指南.pdf');var file4 = new File('MySQL基礎(chǔ).pdf');var file5 = new File('Web安全.pdf');var file6 = new File('Linux菜鳥.pdf');css.add(file2);fe.add(file1).add(file3).add(css).add(js);rd.add(file4).add(file5);web.add(file6);rd.remove(file4);// 掃描web.scan();掃描結(jié)果為







4. 優(yōu)缺點

優(yōu)點

可 以方便地構(gòu)造一棵樹來表示對象的部分-整體 結(jié)構(gòu)。在樹的構(gòu)造最終 完成之后,只需要通過請求樹的最頂層對 象,便能對整棵樹做統(tǒng)一一致的操作。

缺點

創(chuàng)建出來的對象長得都差不多,可能會使代碼不好理解,創(chuàng)建太多的對象對性能也會有一些影響

八、模板方法模式

1. 定義

模板方法模式由兩部分結(jié)構(gòu)組成,第一部分是抽象父類,第二部分是具體的實現(xiàn)子類。

2. 核心

在抽象父類中封裝子類的算法框架,它的 init方法可作為一個算法的模板,指導(dǎo)子類以何種順序去執(zhí)行哪些方法。

由父類分離出公共部分,要求子類重寫某些父類的(易變化的)抽象方法

3. 實現(xiàn)

模板方法模式一般的實現(xiàn)方式為繼承

以運動作為例子,運動有比較通用的一些處理,這部分可以抽離開來,在父類中實現(xiàn)。具體某項運動的特殊性則有自類來重寫實現(xiàn)。

最終子類直接調(diào)用父類的模板函數(shù)來執(zhí)行

// 體育運動function Sport() {}Sport.prototype = { constructor: Sport, // 模板,按順序執(zhí)行 init: function() { this.stretch(); this.jog(); this.deepBreath(); this.start(); var free = this.end(); // 運動后還有空的話,就拉伸一下 if (free !== false) { this.stretch(); } }, // 拉伸 stretch: function() { console.log('拉伸'); }, // 慢跑 jog: function() { console.log('慢跑'); }, // 深呼吸 deepBreath: function() { console.log('深呼吸'); }, // 開始運動 start: function() { throw new Error('子類必須重寫此方法'); }, // 結(jié)束運動 end: function() { console.log('運動結(jié)束'); }};// 籃球function Basketball() {}Basketball.prototype = new Sport();// 重寫相關(guān)的方法Basketball.prototype.start = function() { console.log('先投上幾個三分');};Basketball.prototype.end = function() { console.log('運動結(jié)束了,有事先走一步'); return false;};// 馬拉松function Marathon() {}Marathon.prototype = new Sport();var basketball = new Basketball();var marathon = new Marathon();// 子類調(diào)用,最終會按照父類定義的順序執(zhí)行basketball.init();marathon.init();





繼續(xù)閱讀 下半部分





關(guān)鍵詞:設(shè)計,模式,中常

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉