最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解JavaScript系列(7) S.O.L.I.D五大原則之開閉原則OCP

 更新時間:2012年01月15日 23:25:30   作者:  
本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第2篇,開閉原則OCP(The Open/Closed Principle )。
前言
本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第2篇,開閉原則OCP(The Open/Closed Principle )。
開閉原則的描述是:
Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
軟件實(shí)體(類,模塊,方法等等)應(yīng)當(dāng)對擴(kuò)展開放,對修改關(guān)閉,即軟件實(shí)體應(yīng)當(dāng)在不修改的前提下擴(kuò)展。
復(fù)制代碼
open for extension(對擴(kuò)展開放)的意思是說當(dāng)新需求出現(xiàn)的時候,可以通過擴(kuò)展現(xiàn)有模型達(dá)到目的。而Close for modification(對修改關(guān)閉)的意思是說不允許對該實(shí)體做任何修改,說白了,就是這些需要執(zhí)行多樣行為的實(shí)體應(yīng)該設(shè)計成不需要修改就可以實(shí)現(xiàn)各種的變化,堅持開閉原則有利于用最少的代碼進(jìn)行項(xiàng)目維護(hù)。
英文原文:http://freshbrewedcode.com/derekgreer/2011/12/19/solid-javascript-the-openclosed-principle/
問題代碼
為了直觀地描述,我們來舉個例子演示一下,下屬代碼是動態(tài)展示question列表的代碼(沒有使用開閉原則)。
復(fù)制代碼 代碼如下:

// 問題類型
var AnswerType = {
Choice: 0,
Input: 1
};
// 問題實(shí)體
function question(label, answerType, choices) {
return {
label: label,
answerType: answerType,
choices: choices // 這里的choices是可選參數(shù)
};
}
var view = (function () {
// render一個問題
function renderQuestion(target, question) {
var questionWrapper = document.createElement('div');
questionWrapper.className = 'question';
var questionLabel = document.createElement('div');
questionLabel.className = 'question-label';
var label = document.createTextNode(question.label);
questionLabel.appendChild(label);
var answer = document.createElement('div');
answer.className = 'question-input';
// 根據(jù)不同的類型展示不同的代碼:分別是下拉菜單和輸入框兩種
if (question.answerType === AnswerType.Choice) {
var input = document.createElement('select');
var len = question.choices.length;
for (var i = 0; i < len; i++) {
var option = document.createElement('option');
option.text = question.choices[i];
option.value = question.choices[i];
input.appendChild(option);
}
}
else if (question.answerType === AnswerType.Input) {
var input = document.createElement('input');
input.type = 'text';
}
answer.appendChild(input);
questionWrapper.appendChild(questionLabel);
questionWrapper.appendChild(answer);
target.appendChild(questionWrapper);
}
return {
// 遍歷所有的問題列表進(jìn)行展示
render: function (target, questions) {
for (var i = 0; i < questions.length; i++) {
renderQuestion(target, questions[i]);
};
}
};
})();
var questions = [
question('Have you used tobacco products within the last 30 days?', AnswerType.Choice, ['Yes', 'No']),
question('What medications are you currently using?', AnswerType.Input)
];
var questionRegion = document.getElementById('questions');
view.render(questionRegion, questions);

上面的代碼,view對象里包含一個render方法用來展示question列表,展示的時候根據(jù)不同的question類型使用不同的展示方式,一個question包含一個label和一個問題類型以及choices的選項(xiàng)(如果是選擇類型的話)。如果問題類型是Choice那就根據(jù)選項(xiàng)生產(chǎn)一個下拉菜單,如果類型是Input,那就簡單地展示input輸入框。
該代碼有一個限制,就是如果再增加一個question類型的話,那就需要再次修改renderQuestion里的條件語句,這明顯違反了開閉原則。
重構(gòu)代碼
讓我們來重構(gòu)一下這個代碼,以便在出現(xiàn)新question類型的情況下允許擴(kuò)展view對象的render能力,而不需要修改view對象內(nèi)部的代碼。
先來創(chuàng)建一個通用的questionCreator函數(shù):
復(fù)制代碼 代碼如下:

function questionCreator(spec, my) {
var that = {};
my = my || {};
my.label = spec.label;
my.renderInput = function () {
throw "not implemented";
// 這里renderInput沒有實(shí)現(xiàn),主要目的是讓各自問題類型的實(shí)現(xiàn)代碼去覆蓋整個方法
};
that.render = function (target) {
var questionWrapper = document.createElement('div');
questionWrapper.className = 'question';
var questionLabel = document.createElement('div');
questionLabel.className = 'question-label';
var label = document.createTextNode(spec.label);
questionLabel.appendChild(label);
var answer = my.renderInput();
// 該render方法是同樣的粗合理代碼
// 唯一的不同就是上面的一句my.renderInput()
// 因?yàn)椴煌膯栴}類型有不同的實(shí)現(xiàn)
questionWrapper.appendChild(questionLabel);
questionWrapper.appendChild(answer);
return questionWrapper;
};
return that;
}

該代碼的作用組合要是render一個問題,同時提供一個未實(shí)現(xiàn)的renderInput方法以便其他function可以覆蓋,以使用不同的問題類型,我們繼續(xù)看一下每個問題類型的實(shí)現(xiàn)代碼:
復(fù)制代碼 代碼如下:

function choiceQuestionCreator(spec) {
var my = {},
that = questionCreator(spec, my);
// choice類型的renderInput實(shí)現(xiàn)
my.renderInput = function () {
var input = document.createElement('select');
var len = spec.choices.length;
for (var i = 0; i < len; i++) {
var option = document.createElement('option');
option.text = spec.choices[i];
option.value = spec.choices[i];
input.appendChild(option);
}
return input;
};
return that;
}
function inputQuestionCreator(spec) {
var my = {},
that = questionCreator(spec, my);
// input類型的renderInput實(shí)現(xiàn)
my.renderInput = function () {
var input = document.createElement('input');
input.type = 'text';
return input;
};
return that;
}

choiceQuestionCreator函數(shù)和inputQuestionCreator函數(shù)分別對應(yīng)下拉菜單和input輸入框的renderInput實(shí)現(xiàn),通過內(nèi)部調(diào)用統(tǒng)一的questionCreator(spec, my)然后返回that對象(同一類型哦)。
view對象的代碼就很固定了。
復(fù)制代碼 代碼如下:

var view = {
render: function(target, questions) {
for (var i = 0; i < questions.length; i++) {
target.appendChild(questions[i].render());
}
}
};

所以我們聲明問題的時候只需要這樣做,就OK了:
復(fù)制代碼 代碼如下:

var questions = [
choiceQuestionCreator({
label: 'Have you used tobacco products within the last 30 days?',
choices: ['Yes', 'No']
  }),
inputQuestionCreator({
label: 'What medications are you currently using?'
  })
];

最終的使用代碼,我們可以這樣來用:
復(fù)制代碼 代碼如下:

var questionRegion = document.getElementById('questions');
view.render(questionRegion, questions);


重構(gòu)后的最終代碼
復(fù)制代碼 代碼如下:

function questionCreator(spec, my) {
var that = {};
my = my || {};
my.label = spec.label;
my.renderInput = function() {
throw "not implemented";
};
that.render = function(target) {
var questionWrapper = document.createElement('div');
questionWrapper.className = 'question';
var questionLabel = document.createElement('div');
questionLabel.className = 'question-label';
var label = document.createTextNode(spec.label);
questionLabel.appendChild(label);
var answer = my.renderInput();
questionWrapper.appendChild(questionLabel);
questionWrapper.appendChild(answer);
return questionWrapper;
};
return that;
}
function choiceQuestionCreator(spec) {
var my = {},
that = questionCreator(spec, my);
my.renderInput = function() {
var input = document.createElement('select');
var len = spec.choices.length;
for (var i = 0; i < len; i++) {
var option = document.createElement('option');
option.text = spec.choices[i];
option.value = spec.choices[i];
input.appendChild(option);
}
return input;
};
return that;
}
function inputQuestionCreator(spec) {
var my = {},
that = questionCreator(spec, my);
my.renderInput = function() {
var input = document.createElement('input');
input.type = 'text';
return input;
};
return that;
}
var view = {
render: function(target, questions) {
for (var i = 0; i < questions.length; i++) {
target.appendChild(questions[i].render());
}
}
};
var questions = [
choiceQuestionCreator({
label: 'Have you used tobacco products within the last 30 days?',
choices: ['Yes', 'No']
}),
inputQuestionCreator({
label: 'What medications are you currently using?'
})
];
var questionRegion = document.getElementById('questions');
view.render(questionRegion, questions);

上面的代碼里應(yīng)用了一些技術(shù)點(diǎn),我們來逐一看一下:
首先,questionCreator方法的創(chuàng)建,可以讓我們使用模板方法模式將處理問題的功能delegat給針對每個問題類型的擴(kuò)展代碼renderInput上。
其次,我們用一個私有的spec屬性替換掉了前面question方法的構(gòu)造函數(shù)屬性,因?yàn)槲覀兎庋b了render行為進(jìn)行操作,不再需要把這些屬性暴露給外部代碼了。
第三,我們?yōu)槊總€問題類型創(chuàng)建一個對象進(jìn)行各自的代碼實(shí)現(xiàn),但每個實(shí)現(xiàn)里都必須包含renderInput方法以便覆蓋questionCreator方法里的renderInput代碼,這就是我們常說的策略模式。
通過重構(gòu),我們可以去除不必要的問題類型的枚舉AnswerType,而且可以讓choices作為choiceQuestionCreator函數(shù)的必選參數(shù)(之前的版本是一個可選參數(shù))。
總結(jié)
重構(gòu)以后的版本的view對象可以很清晰地進(jìn)行新的擴(kuò)展了,為不同的問題類型擴(kuò)展新的對象,然后聲明questions集合的時候再里面指定類型就行了,view對象本身不再修改任何改變,從而達(dá)到了開閉原則的要求。
另:懂C#的話,不知道看了上面的代碼后是否和多態(tài)的實(shí)現(xiàn)有些類似?其實(shí)上述的代碼用原型也是可以實(shí)現(xiàn)的,大家可以自行研究一下。

相關(guān)文章

  • 小程序轉(zhuǎn)發(fā)探索示例

    小程序轉(zhuǎn)發(fā)探索示例

    這篇文章主要介紹了小程序轉(zhuǎn)發(fā)探索示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • JavaScript如何動態(tài)創(chuàng)建table表格

    JavaScript如何動態(tài)創(chuàng)建table表格

    這篇文章主要介紹了JavaScript如何動態(tài)創(chuàng)建table表格,一些時候需要動態(tài)的創(chuàng)建和刪除表格,接下來的文章中將為大家介紹下javascript是如何做到的,感興趣的朋友不要錯過
    2015-11-11
  • 深入理解JavaScript中的for循環(huán)

    深入理解JavaScript中的for循環(huán)

    這篇文章主要給大家深入的介紹了JavaScript中的for循環(huán),其中包括ES5中的三種for循環(huán),分別是簡單for循環(huán)、for-in以及forEach,另外還詳細(xì)介紹了ES6新增的一種循環(huán):for-of ,有需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-02-02
  • JS搜狐面試題分析

    JS搜狐面試題分析

    這篇文章主要介紹了JS搜狐面試題,涉及javascript數(shù)組、字符串及面向?qū)ο蟪绦蛟O(shè)計相關(guān)技巧,需要的朋友可以參考下
    2016-12-12
  • 微信小程序自定義彈窗滾動與頁面滾動沖突的解決方法

    微信小程序自定義彈窗滾動與頁面滾動沖突的解決方法

    這篇文章主要為大家詳細(xì)介紹了微信小程序自定義彈窗滾動與頁面滾動沖突的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • JavaScript高階函數(shù)_動力節(jié)點(diǎn)Java學(xué)院整理

    JavaScript高階函數(shù)_動力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了JavaScript高階函數(shù),詳細(xì)講解了什么是高階函數(shù)和高階函數(shù)的用法,有興趣的可以了解下
    2017-06-06
  • 在子窗口中關(guān)閉父窗口的一句代碼

    在子窗口中關(guān)閉父窗口的一句代碼

    在子窗口中關(guān)閉父窗口在某些特殊的情況下還是有存在的必要的,其實(shí)很簡單,只需一句代碼便可實(shí)現(xiàn),感興趣的朋友可以了解下
    2013-10-10
  • TypeScript?背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解

    TypeScript?背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解

    這篇文章主要為大家介紹了TypeScript?背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 淺析微信小程序modal彈窗關(guān)閉默認(rèn)會執(zhí)行cancel問題

    淺析微信小程序modal彈窗關(guān)閉默認(rèn)會執(zhí)行cancel問題

    這篇文章主要介紹了小程序modal彈窗關(guān)閉默認(rèn)會執(zhí)行cancel方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10
  • Javascript 獲取滾動條位置等信息的函數(shù)

    Javascript 獲取滾動條位置等信息的函數(shù)

    有時為了準(zhǔn)確定位一個元素,我們需要獲取滾動條的位置,這種需求經(jīng)常出現(xiàn)在 tooltip 和 拖放等應(yīng)用中,其實(shí)這個技術(shù)很簡單,關(guān)鍵是要考慮瀏覽器的兼容性,本文就是介紹這一問題的解決方法。
    2009-09-09

最新評論

保德县| 乐陵市| 新巴尔虎左旗| 阳谷县| 鄢陵县| 安岳县| 忻城县| 项城市| 福建省| 米易县| 新田县| 潜江市| 凤阳县| 湾仔区| 鄯善县| 宁强县| 宁波市| 明溪县| 大埔县| 阿拉善左旗| 辛集市| 抚宁县| 尤溪县| 聂拉木县| 建湖县| 桃江县| 阿勒泰市| 娄底市| 长宁县| 哈巴河县| 三亚市| 达尔| 杨浦区| 延长县| 西畴县| 达尔| 尼木县| 清苑县| 娄底市| 隆安县| 青神县|