JavaScriptシリーズを深く理解する(44):デザインモードのブリッジモード

48157 ワード

紹介する
ブリッジモードは抽象部分とその実現部分を分離して、それらを独立に変化させます.
本文
ブリッジモードは最もよく使われています.まずコードを見ます.
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は任意に入ることができ、また、カスタム処理関数としてのcalback関数も提供される.しかし、これはブリッジと何の関係がありますか?これは下のコードが表すものです.
addEvent(element, 'click', getBeerByIdBridge);
  function getBeerByIdBridge (e) {
    getBeerById(this.id, function(beer) {
      console.log('Requested Beer: '+beer);
  });
}
ここのgetBeerByIdBridgeは、抽象的なclickイベントとgetBeerByIdを接続しながら、イベントソースのIDと、カスタムのcall関数(consolie.log出力)をパラメータとしてgetBeerById関数に導入するための橋です.
この例は簡単に見えるので、もう一つ複雑な実戦例を紹介します.
実戦XHR接続列
私たちは行列を作りたいです.行列の中にajaxがたくさん置いてあります.行列を使うのは主に先に加入した要求を確保するためです.いつでも、要求を一時停止し、削除し、要求を再試行し、各要求に対する購読イベントをサポートすることができます.
ベースコア関数
正式に始まる前に、まずコアのいくつかのパッケージ関数を定義します.最初は非同期要求の関数パッケージです.
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つの方法を追加します.巡回用、フィルタ用の1つです.
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;
});
}
この2つの機能をサポートしている新型ブラウザがあるので、先に判断して、すでにサポートしているなら、もう処理しません.
観測者システム
観察者は列の中のイベントの過程で重要な役割を果たしています.行列処理(成功、失敗、保留)でイベントを購読できます.
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.Que.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、set RetryCount、set Timeout、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>
この例では、flaushの列を作って、一時停止して、列の中の要求を削除して、列を空けるなどの各種の動作をしてもいいです.
締め括りをつける
ブリッジモードの長所も明らかです.主ないくつかの長所を挙げます.
  • 分離インターフェースと実装部分は、1つの実装が一定ではなく、1つのインターフェースに結合されています.抽象的なクラス(関数)の実装は、実行時に配置されてもよく、1つのオブジェクトは、実行時にその実現を変更することもできます.抽象的かつ実現されても十分に結合され、階層化に有利であり、より良い構造化システムが生成されます.
  • 拡張可能性を高める
  • は、詳細がお客様に透明であることを実現し、お客様に詳細を隠すことができます.
  • ブリッジモードは自分の欠点もあります.
    大量のクラスは開発コストの増加につながり、性能面でも減少する可能性があります.
    同期と推奨
    ここではディレクトリインデックスに同期しました.JavaScriptシリーズを深く理解する
    JavaScriptシリーズの文章を深く理解して、オリジナル、翻訳、転載などの各タイプの文章を含んでいます.もしあなたに役立つなら、おじさんに書く力を貸してください.