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

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

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

      深入理解JavaScript系列(44):設計模式之橋接模式

      2012-04-19 07:08  湯姆大叔  閱讀(15136)  評論(14)    收藏  舉報

      介紹

      橋接模式(Bridge)將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

      正文

      橋接模式最常用在事件監控上,先看一段代碼:

      addEvent(element, 'click', getBeerById);
      function getBeerById(e) {
      var id = this.id;
      asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
      // Callback response.
      console.log('Requested Beer: ' + resp.responseText);
      });
      }

      上述代碼,有個問題就是getBeerById必須要有瀏覽器的上下文才能使用,因為其內部使用了this.id這個屬性,如果沒用上下文,那就歇菜了。所以說一般稍微有經驗的程序員都會將程序改造成如下形式:

      function getBeerById(id, callback) {
      // 通過ID發送請求,然后返回數據
      asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
      // callback response
      callback(resp.responseText);
      });
      }

      實用多了,對吧?首先ID可以隨意傳入,而且還提供了一個callback函數用于自定義處理函數。但是這個和橋接有什么關系呢?這就是下段代碼所要體現的了:

      addEvent(element, 'click', getBeerByIdBridge);
        function getBeerByIdBridge (e) {
          getBeerById(this.id, function(beer) {
            console.log('Requested Beer: '+beer);
        });
      }

      這里的getBeerByIdBridge就是我們定義的橋,用于將抽象的click事件和getBeerById連接起來,同時將事件源的ID,以及自定義的call函數(console.log輸出)作為參數傳入到getBeerById函數里。

      這個例子看起來有些簡單,我們再來一個復雜點的實戰例子。

      實戰XHR連接隊列

      我們要構建一個隊列,隊列里存放了很多ajax請求,使用隊列(queue)主要是因為要確保先加入的請求先被處理。任何時候,我們可以暫停請求、刪除請求、重試請求以及支持對各個請求的訂閱事件。

      基礎核心函數

      在正式開始之前,我們先定義一下核心的幾個封裝函數,首先第一個是異步請求的函數封裝:

      var asyncRequest = (function () {
      function handleReadyState(o, callback) {
      var poll = window.setInterval(
      function () {
      if (o && o.readyState == 4) {
      window.clearInterval(poll);
      if (callback) {
      callback(o);
      }
      }
      },
      50
      );
      }

      var getXHR = function () {
      var http;
      try {
      http = new XMLHttpRequest;
      getXHR = function () {
      return new XMLHttpRequest;
      };
      }

      catch (e) {
      var msxml = [
      'MSXML2.XMLHTTP.3.0',
      'MSXML2.XMLHTTP',
      'Microsoft.XMLHTTP'
      ];

      for (var i = 0, len = msxml.length; i < len; ++i) {
      try {
      http = new ActiveXObject(msxml[i]);
      getXHR = function () {
      return new ActiveXObject(msxml[i]);
      };
      break;
      }
      catch (e) { }
      }
      }
      return http;
      };

      return function (method, uri, callback, postData) {
      var http = getXHR();
      http.open(method, uri, true);
      handleReadyState(http, callback);
      http.send(postData || null);
      return http;
      };
      })();

      上述封裝的自執行函數是一個通用的Ajax請求函數,相信屬性Ajax的人都能看懂了。

      接下來我們定義一個通用的添加方法(函數)的方法:

      Function.prototype.method = function (name, fn) {
      this.prototype[name] = fn;
      return this;
      };

      最后再添加關于數組的2個方法,一個用于遍歷,一個用于篩選:

      if (!Array.prototype.forEach) {
      Array.method('forEach', function (fn, thisObj) {
      var scope = thisObj || window;
      for (var i = 0, len = this.length; i < len; ++i) {
      fn.call(scope, this[i], i, this);
      }
      });
      }

      if (!Array.prototype.filter) {
      Array.method('filter', function (fn, thisObj) {
      var scope = thisObj || window;
      var a = [];
      for (var i = 0, len = this.length; i < len; ++i) {
      if (!fn.call(scope, this[i], i, this)) {
      continue;
      }
      a.push(this[i]);
      }
      return a;
      });
      }

      因為有的新型瀏覽器已經支持了這兩種功能(或者有些類庫已經支持了),所以要先判斷,如果已經支持的話,就不再處理了。

      觀察者系統

      觀察者在隊列里的事件過程中扮演著重要的角色,可以隊列處理時(成功、失敗、掛起)訂閱事件:

      window.DED = window.DED || {};
      DED.util = DED.util || {};
      DED.util.Observer = function () {
      this.fns = [];
      }

      DED.util.Observer.prototype = {
      subscribe: function (fn) {
      this.fns.push(fn);
      },

      unsubscribe: function (fn) {
      this.fns = this.fns.filter(
      function (el) {
      if (el !== fn) {
      return el;
      }
      }
      );
      },
      fire: function (o) {
      this.fns.forEach(
      function (el) {
      el(o);
      }
      );
      }
      };

      隊列主要實現代碼

      首先訂閱了隊列的主要屬性和事件委托:

      DED.Queue = function () {
      // 包含請求的隊列.
      this.queue = [];
      // 使用Observable對象在3個不同的狀態上,以便可以隨時訂閱事件
      this.onComplete = new DED.util.Observer;
      this.onFailure = new DED.util.Observer;
      this.onFlush = new DED.util.Observer;

      // 核心屬性,可以在外部調用的時候進行設置
      this.retryCount = 3;
      this.currentRetry = 0;
      this.paused = false;
      this.timeout = 5000;
      this.conn = {};
      this.timer = {};
      };

      然后通過DED.Queue.method的鏈式調用,則隊列上添加了很多可用的方法:

      DED.Queue.
      method('flush', function () {
      // flush方法
      if (!this.queue.length > 0) {
      return;
      }

      if (this.paused) {
      this.paused = false;
      return;
      }

      var that = this;
      this.currentRetry++;
      var abort = function () {
      that.conn.abort();
      if (that.currentRetry == that.retryCount) {
      that.onFailure.fire();
      that.currentRetry = 0;
      } else {
      that.flush();
      }
      };

      this.timer = window.setTimeout(abort, this.timeout);
      var callback = function (o) {
      window.clearTimeout(that.timer);
      that.currentRetry = 0;
      that.queue.shift();
      that.onFlush.fire(o.responseText);
      if (that.queue.length == 0) {
      that.onComplete.fire();
      return;
      }

      // recursive call to flush
      that.flush();

      };

      this.conn = asyncRequest(
      this.queue[0]['method'],
      this.queue[0]['uri'],
      callback,
      this.queue[0]['params']
      );
      }).
      method('setRetryCount', function (count) {
      this.retryCount = count;
      }).
      method('setTimeout', function (time) {
      this.timeout = time;
      }).
      method('add', function (o) {
      this.queue.push(o);
      }).
      method('pause', function () {
      this.paused = true;
      }).
      method('dequeue', function () {
      this.queue.pop();
      }).
      method('clear', function () {
      this.queue = [];
      });

      代碼看起來很多,折疊以后就可以發現,其實就是在隊列上定義了flush, setRetryCount, setTimeout, add, pause, dequeue, 和clear方法。

      簡單調用

      var q = new DED.Queue;
      // 設置重試次數高一點,以便應付慢的連接
      q.setRetryCount(5);
      // 設置timeout時間
      q.setTimeout(1000);
      // 添加2個請求.
      q.add({
      method: 'GET',
      uri: '/path/to/file.php?ajax=true'
      });

      q.add({
      method: 'GET',
      uri: '/path/to/file.php?ajax=true&woe=me'
      });

      // flush隊列
      q.flush();
      // 暫停隊列,剩余的保存
      q.pause();
      // 清空.
      q.clear();
      // 添加2個請求.
      q.add({
      method: 'GET',
      uri: '/path/to/file.php?ajax=true'
      });

      q.add({
      method: 'GET',
      uri: '/path/to/file.php?ajax=true&woe=me'
      });

      // 從隊列里刪除最后一個請求.
      q.dequeue();
      // 再次Flush
      q.flush();

      橋接呢?

      上面的調用代碼里并沒有橋接,那橋呢?看一下下面的完整示例,就可以發現處處都有橋哦:

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
      "http://www.w3.org/TR/html4/strict.dtd"
      >
      <html>
      <head>
      <meta http-equiv="Content-type" content="text/html; charset=utf-8">
      <title>Ajax Connection Queue</title>
      <script src="utils.js"></script>
      <script src="queue.js"></script>
      <script type="text/javascript">
      addEvent(window,
      'load', function () {
      // 實現.
      var q = new DED.Queue;
      q.setRetryCount(
      5);
      q.setTimeout(
      3000);
      var items = $('items');
      var results = $('results');
      var queue = $('queue-items');
      // 在客戶端保存跟蹤自己的請求
      var requests = [];
      // 每個請求flush以后,訂閱特殊的處理步驟
      q.onFlush.subscribe(function (data) {
      results.innerHTML
      = data;
      requests.shift();
      queue.innerHTML
      = requests.toString();
      });
      // 訂閱時間處理步驟
      q.onFailure.subscribe(function () {
      results.innerHTML
      += ' <span style="color:red;">Connection Error!</span>';
      });
      // 訂閱全部成功的處理步驟x
      q.onComplete.subscribe(function () {
      results.innerHTML
      += ' <span style="color:green;">Completed!</span>';
      });
      var actionDispatcher = function (element) {
      switch (element) {
      case 'flush':
      q.flush();
      break;
      case 'dequeue':
      q.dequeue();
      requests.pop();
      queue.innerHTML
      = requests.toString();
      break;
      case 'pause':
      q.pause();
      break;
      case 'clear':
      q.clear();
      requests
      = [];
      queue.innerHTML
      = '';
      break;
      }
      };
      var addRequest = function (request) {
      var data = request.split('-')[1];
      q.add({
      method:
      'GET',
      uri:
      'bridge-connection-queue.php?ajax=true&s=' + data,
      params:
      null
      });
      requests.push(data);
      queue.innerHTML
      = requests.toString();
      };
      addEvent(items,
      'click', function (e) {
      var e = e || window.event;
      var src = e.target || e.srcElement;
      try {
      e.preventDefault();
      }
      catch (ex) {
      e.returnValue
      = false;
      }
      actionDispatcher(src.id);
      });
      var adders = $('adders');
      addEvent(adders,
      'click', function (e) {
      var e = e || window.event;
      var src = e.target || e.srcElement;
      try {
      e.preventDefault();
      }
      catch (ex) {
      e.returnValue
      = false;
      }
      addRequest(src.id);
      });
      });
      </script>
      <style type="text/css" media="screen">
      body
      {
      font
      : 100% georgia,times,serif;
      }
      h1, h2
      {
      font-weight
      : normal;
      }
      #queue-items
      {
      height
      : 1.5em;
      }
      #add-stuff
      {
      padding
      : .5em;
      background
      : #ddd;
      border
      : 1px solid #bbb;
      }
      #results-area
      {
      padding
      : .5em;
      border
      : 1px solid #bbb;
      }
      </style>
      </head>
      <body id="example">
      <div id="doc">
      <h1>
      異步聯接請求</h1>
      <div id="queue-items">
      </div>
      <div id="add-stuff">
      <h2>向隊列里添加新請求</h2>
      <ul id="adders">
      <li><a href="#" id="action-01">添加 "01" 到隊列</a></li>
      <li><a href="#" id="action-02">添加 "02" 到隊列</a></li>
      <li><a href="#" id="action-03">添加 "03" 到隊列</a></li>
      </ul>
      </div>
      <h2>隊列控制</h2>
      <ul id='items'>
      <li><a href="#" id="flush">Flush</a></li>
      <li><a href="#" id="dequeue">出列Dequeue</a></li>
      <li><a href="#" id="pause">暫停Pause</a></li>
      <li><a href="#" id="clear">清空Clear</a></li>
      </ul>
      <div id="results-area">
      <h2>
      結果:
      </h2>
      <div id="results">
      </div>
      </div>
      </div>
      </body>
      </html>

      在這個示例里,你可以做flush隊列,暫停隊列,刪除隊列里的請求,清空隊列等各種動作,同時相信大家也體會到了橋接的威力了。

      總結

      橋接模式的優點也很明顯,我們只列舉主要幾個優點:

      1. 分離接口和實現部分,一個實現未必不變地綁定在一個接口上,抽象類(函數)的實現可以在運行時刻進行配置,一個對象甚至可以在運行時刻改變它的實現,同將抽象和實現也進行了充分的解耦,也有利于分層,從而產生更好的結構化系統。
      2. 提高可擴充性
      3. 實現細節對客戶透明,可以對客戶隱藏實現細節。

      同時橋接模式也有自己的缺點:

      大量的類將導致開發成本的增加,同時在性能方面可能也會有所減少。

      同步與推薦

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

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

      主站蜘蛛池模板: 天堂在线中文| 亚洲av日韩在线资源| 国产360激情盗摄全集| 国产欧美精品一区二区三区-老狼| 久久精品国产午夜福利伦理| 少妇被粗大的猛烈进出动视频 | 国产区成人精品视频| 精品国产迷系列在线观看| 色综合色狠狠天天综合网| 无码中文字幕人妻在线一区| 日韩av日韩av在线| 国产成人精品亚洲精品密奴| 欧美一区二区三区成人久久片| 999精品色在线播放| 亚洲人妻精品一区二区| 97无码人妻福利免费公开在线视频| 无码专区 人妻系列 在线| 彭水| 中文字幕在线国产精品| AV无码免费不卡在线观看| 中文国产乱码在线人妻一区二区| 中文字幕国产精品日韩| 欧美激情一区二区| 国产粉嫩美女一区二区三| 人妻伦理在线一二三区| 国产三级精品三级在线看| 成人免费A级毛片无码网站入口| 97色伦97色伦国产| 豆国产97在线 | 亚洲| 久久亚洲中文字幕伊人久久大 | 罗定市| 性动态图无遮挡试看30秒 | 国产亚洲一二三区精品| 亚洲色成人一区二区三区| 国产成人精品a视频| 欧美肥老太交视频免费| 女人腿张开让男人桶爽| 男女裸体影院高潮| 日本伊人色综合网| 苍井空一区二区三区在线观看| 久久久天堂国产精品女人|