<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

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

      2012-01-09 09:08  湯姆大叔  閱讀(25232)  評論(40)    收藏  舉報

      前言

      本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實現的第2篇,開閉原則OCP(The Open/Closed Principle )。

      開閉原則的描述是:

      Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
      軟件實體(類,模塊,方法等等)應當對擴展開放,對修改關閉,即軟件實體應當在不修改的前提下擴展。

      open for extension(對擴展開放)的意思是說當新需求出現的時候,可以通過擴展現有模型達到目的。而Close for modification(對修改關閉)的意思是說不允許對該實體做任何修改,說白了,就是這些需要執行多樣行為的實體應該設計成不需要修改就可以實現各種的變化,堅持開閉原則有利于用最少的代碼進行項目維護。

      英文原文:http://freshbrewedcode.com/derekgreer/2011/12/19/solid-javascript-the-openclosed-principle/

      問題代碼

      為了直觀地描述,我們來舉個例子演示一下,下屬代碼是動態展示question列表的代碼(沒有使用開閉原則)。

      // 問題類型
      var AnswerType = {
      Choice: 0,
      Input: 1
      };

      // 問題實體
      function question(label, answerType, choices) {
      return {
      label: label,
      answerType: answerType,
      choices: choices // 這里的choices是可選參數
      };
      }

      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';

      // 根據不同的類型展示不同的代碼:分別是下拉菜單和輸入框兩種
      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 {
      // 遍歷所有的問題列表進行展示
      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列表,展示的時候根據不同的question類型使用不同的展示方式,一個question包含一個label和一個問題類型以及choices的選項(如果是選擇類型的話)。如果問題類型是Choice那就根據選項生產一個下拉菜單,如果類型是Input,那就簡單地展示input輸入框。

      該代碼有一個限制,就是如果再增加一個question類型的話,那就需要再次修改renderQuestion里的條件語句,這明顯違反了開閉原則。

      重構代碼

      讓我們來重構一下這個代碼,以便在出現新question類型的情況下允許擴展view對象的render能力,而不需要修改view對象內部的代碼。

      先來創建一個通用的questionCreator函數:

      function questionCreator(spec, my) {
      var that = {};

      my = my || {};
      my.label = spec.label;

      my.renderInput = function () {
      throw "not implemented";
      // 這里renderInput沒有實現,主要目的是讓各自問題類型的實現代碼去覆蓋整個方法
      };

      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()
      // 因為不同的問題類型有不同的實現

      questionWrapper.appendChild(questionLabel);
      questionWrapper.appendChild(answer);
      return questionWrapper;
      };

      return that;
      }

      該代碼的作用組合要是render一個問題,同時提供一個未實現的renderInput方法以便其他function可以覆蓋,以使用不同的問題類型,我們繼續看一下每個問題類型的實現代碼:

      function choiceQuestionCreator(spec) {

      var my = {},
      that = questionCreator(spec, my);

      // choice類型的renderInput實現
      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實現
      my.renderInput = function () {
      var input = document.createElement('input');
      input.type = 'text';
      return input;
      };

      return that;
      }

      choiceQuestionCreator函數和inputQuestionCreator函數分別對應下拉菜單和input輸入框的renderInput實現,通過內部調用統一的questionCreator(spec, my)然后返回that對象(同一類型哦)。

      view對象的代碼就很固定了。

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

      所以我們聲明問題的時候只需要這樣做,就OK了:

      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);

       

      重構后的最終代碼
      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);

      上面的代碼里應用了一些技術點,我們來逐一看一下:

      1. 首先,questionCreator方法的創建,可以讓我們使用模板方法模式將處理問題的功能delegat給針對每個問題類型的擴展代碼renderInput上。
      2. 其次,我們用一個私有的spec屬性替換掉了前面question方法的構造函數屬性,因為我們封裝了render行為進行操作,不再需要把這些屬性暴露給外部代碼了。
      3. 第三,我們為每個問題類型創建一個對象進行各自的代碼實現,但每個實現里都必須包含renderInput方法以便覆蓋questionCreator方法里的renderInput代碼,這就是我們常說的策略模式。

      通過重構,我們可以去除不必要的問題類型的枚舉AnswerType,而且可以讓choices作為choiceQuestionCreator函數的必選參數(之前的版本是一個可選參數)。

      總結

      重構以后的版本的view對象可以很清晰地進行新的擴展了,為不同的問題類型擴展新的對象,然后聲明questions集合的時候再里面指定類型就行了,view對象本身不再修改任何改變,從而達到了開閉原則的要求。

      另:懂C#的話,不知道看了上面的代碼后是否和多態的實現有些類似?其實上述的代碼用原型也是可以實現的,大家可以自行研究一下。

      同步與推薦

      本文已同步至目錄索引:深入理解JavaScript系列

      深入理解JavaScript系列文章,包括了原創,翻譯,轉載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。

      主站蜘蛛池模板: 高清不卡一区二区三区| 资源在线观看视频一区二区 | 亚洲色大成网站www久久九九| 你拍自拍亚洲一区二区三区| 国产日韩一区二区在线看| 日本黄页网站免费观看| 久久精品不卡一区二区| 亚洲性日韩精品一区二区| 国产白嫩护士在线播放| 美女高潮黄又色高清视频免费| 亚洲精品成人片在线观看精品字幕 | 中文字幕亚洲综合第一页| 国产精品免费中文字幕| 国产精品午夜福利在线观看| 免费看男女做好爽好硬视频| 国产日韩精品中文字幕| 国产人妻精品午夜福利免费| 五月丁香综合缴情六月小说| 石景山区| 日本一区二区精品色超碰| 亚洲熟妇自偷自拍另类| 壤塘县| 风流老熟女一区二区三区| 一区二区三区鲁丝不卡| 久久精品亚洲中文字幕无码网站 | 国产自拍偷拍视频在线观看 | 午夜免费福利小电影| 最近免费中文字幕大全| 国产成人亚洲欧美二区综合| 左贡县| 精品久久久久无码| A三级三级成人网站在线视频| 扎鲁特旗| 四虎影院176| 国产成人午夜福利在线播放 | 成人精品一区日本无码网| 99久久激情国产精品| 亚洲午夜亚洲精品国产成人| 国产av亚洲精品ai换脸电影| 欧美亚洲精品中文字幕乱码| 亚洲老女人区一区二视频|