摘要:橋接實(shí)現(xiàn)的時(shí)候橋接模式非常有用可能正是由于這個(gè)該模式使用地不夠廣泛在設(shè)計(jì)時(shí)該模式可以弱化與使用它的類和對(duì)象之間的耦合該模式的作用在于將抽象與其實(shí)現(xiàn)隔離開讓他們獨(dú)立變化而且對(duì)于事件驅(qū)動(dòng)編程有許多好處有以及其他基于動(dòng)作的方法無論它們是用來創(chuàng)建鼓
橋接
實(shí)現(xiàn) API 的時(shí)候,橋接模式非常有用,可能正是由于這個(gè),該模式使用地不夠廣泛.在設(shè)計(jì) js API 時(shí),該模式可以弱化API 與使用它的類和對(duì)象之間的耦合. 該模式的作用在于**將抽象與其實(shí)現(xiàn)隔離開,讓他們獨(dú)立變化**.而且對(duì)于事件驅(qū)動(dòng)編程有許多好處. js API有 getter, setter, requester 以及其他基于動(dòng)作的方法.無論它們是用來創(chuàng)建 Web 鼓舞 API 還是普通 accessor 和 mutator.在實(shí)現(xiàn)過程中橋接模式都有助于保持 API 代碼的簡(jiǎn)潔.事件監(jiān)聽器
最常見的應(yīng)用場(chǎng)合之一是事件監(jiān)聽回調(diào).假設(shè)有一個(gè) API 函數(shù) getBeerById, 他根據(jù)一個(gè)標(biāo)識(shí)符返回有關(guān)啤酒的信息.
addEvent(ele, "click", getBeerById); function getBeerById(e) { var id = this.id; asyncRequest("GET", "beer.url?id=" + id, function(resp) { // Callback response. console.log("Requested Beer: " + resp.responseText); }); }
這個(gè) API 只能工作在瀏覽器中,根據(jù)事件監(jiān)聽器回調(diào)函數(shù)的機(jī)制,事件對(duì)象自然會(huì)被作為第一個(gè)參數(shù)傳遞給這個(gè)函數(shù),在本例中沒有使用這個(gè)參數(shù),只是從 this 對(duì)象獲取 id.那么如果在命令行環(huán)境運(yùn)行它就失效不起作用.
function getBeerById(id, callback) { // Make request for beer by ID, then return the beer data. asyncRequest("GET", "beer.url?id=" + id, function (resp) { // callback reponse callback(resp.responseText); }); } addEvent(ele, "click", getBeerByIdBridge); function getBeerByIdBridge(e) { getBeerById(this.id, function (beer) { console.log("Requested Beer: " + beer); }); }
這樣就只針對(duì)接口而不是實(shí)現(xiàn)進(jìn)行編程,用橋接模式把抽象隔離開來.
有了這層橋接元素,API 的適用范圍大大拓寬,現(xiàn)在的 getBeerById 沒有和事件對(duì)象綁定在一起,可以在單元測(cè)試中運(yùn)行該 API,只需提供一個(gè) ID 和對(duì)調(diào)函數(shù).現(xiàn)在可以在命令行環(huán)境運(yùn)行這個(gè)接口.
除了在事件回調(diào)函數(shù)與接口之間進(jìn)行橋接外,橋接模式還可以用連接公開的 API代碼和私有的實(shí)現(xiàn)代碼.另外,還可以連接多個(gè)類.從類的角度看,意味著把接口作為公開的代碼編寫,把類的實(shí)現(xiàn)作為私有代碼編寫.
可以使用一些特權(quán)方法來做橋梁以便訪問私有變量空間.第3章已經(jīng)見到過.
var Public = function () { var secretNum = 3; this.privilegedGetter = function () { return secretNum; }; }; var p = new Public(); var data = p.privilegedGetter();
連接多個(gè)類:
var Class1 = function (a, b, c) { this.a = a; this.b = b; this.c = c; }; var Class2 = function (d) { this.d = d; } var BridgeClass = function (a, b, c, d) { this.one = new Class1(a, b, c); this.two = new Class2(d); }
在這里,橋接類是一個(gè)門面類,不同的是橋接模式能夠讓Class1和 Class2獨(dú)立于 BridgeClass 而發(fā)生變化,和門面類不同.
適用場(chǎng)合事件驅(qū)動(dòng)編程必備.不過我們喜歡事件驅(qū)動(dòng)開發(fā)的函數(shù)式風(fēng)格,卻忘了編寫接口.判斷什么時(shí)候是用橋接模式很簡(jiǎn)單:
$("#example-id").on("click", function () { new RichTextEditor(); })l
現(xiàn)在沒法看出編輯器要顯示在哪,有什么配置項(xiàng),還有如何修改.要做的是讓接口"可橋接"(可適配)...
還有就是之前講到的特權(quán)函數(shù)用來訪問私有數(shù)據(jù).
利讓 API 更強(qiáng)壯,提高組件的模塊化程度.把抽象和其實(shí)現(xiàn)隔離開,有助于獨(dú)立管理,而且Bug 更容易查找.
弊每使用一個(gè)橋接元素都要增加一次函數(shù)調(diào)用,提高系統(tǒng)復(fù)雜度,影響性能,不要濫用,舉例來說,如果一個(gè)橋接函數(shù)被用于連接兩個(gè)函數(shù),但是其中某個(gè)函數(shù)根本不會(huì)在橋接函數(shù)之外被調(diào)用,那么該橋接函數(shù)可以不用.
----------next part----------
組合同一條命令在多個(gè)對(duì)象上引發(fā)復(fù)雜或者遞歸的操作.
兩大好處:
用同樣的方法處理對(duì)象集合和其中的特定子對(duì)象.組合對(duì)象 composite 和組成它的對(duì)象實(shí)現(xiàn)了同批操作.是一種向下傳遞的操作性質(zhì).
把一批子對(duì)象組織成樹形結(jié)構(gòu),使整棵樹都可以被遍歷.所有組合對(duì)象都實(shí)現(xiàn)了一個(gè)用來獲取子對(duì)象的方法.
組合對(duì)象的結(jié)構(gòu)組合對(duì)象的層次體系中有兩種類型的對(duì)象:葉對(duì)象和組合對(duì)象,是一個(gè)遞歸定義:一個(gè)組合對(duì)象由一些別的組合對(duì)象和葉對(duì)象組成.只有葉對(duì)象不再包含子對(duì)象,它是組合對(duì)象中最基本的元素,也落實(shí)了各種操作.(樹是數(shù)據(jù)結(jié)構(gòu)中較為基本的概念,這里的概念應(yīng)該不難理解的)
使用必須同時(shí)具備兩個(gè)條件:
存在一批組織成某種層次體系的對(duì)象
希望對(duì)這批對(duì)象或者其中一部分對(duì)象進(jìn)行一個(gè)共同操作.
組合模式擅長(zhǎng)于對(duì)大批對(duì)象進(jìn)行操作.組織這類對(duì)象并把操作從一個(gè)層次向下一個(gè)層次傳遞,可以弱化對(duì)象間的耦合并且可以互換的使用一些類或者實(shí)例.
要求創(chuàng)建一個(gè)表單,可以保存,恢復(fù),驗(yàn)證值.看似不復(fù)雜,但是主要問題在于表單中元素的內(nèi)容和數(shù)目完全未知,而且因用戶而異.緊密耦合到 Name 和 Address 特定表單域的 validate 函數(shù)不會(huì)起作用,因?yàn)闊o法驗(yàn)證哪些域...
現(xiàn)在是組合模式大展身手的時(shí)候,首先,我們要逐個(gè)驗(yàn)證表單的各個(gè)組成元素,判斷屬于組合對(duì)象還是葉對(duì)象.表單最基本的構(gòu)成要素是用戶用于輸入數(shù)據(jù)的域,由input, select,textarea 等等組成.上面一層是用于組織域的 fieldset 標(biāo)簽.最頂層是表單自身.
葉對(duì)象 (input) --| | -- 組合對(duì)象 (filedset) | 葉對(duì)象 (input) --| | |=組合對(duì)象 (form) 葉對(duì)象 (select) --| | --組合對(duì)象 (fieldset) | 葉對(duì)象 (textarea)--| |
組合對(duì)象和其所有子對(duì)象都具有相同的接口,可能有人會(huì)把他們看成超類和子類的關(guān)系,但是并非如此,葉對(duì)象沒有繼承上一級(jí)組合對(duì)象.
首先是創(chuàng)建一個(gè)動(dòng)態(tài)表單并且實(shí)現(xiàn)save 和 validate 操作.表單中實(shí)際擁有的域因用戶而異,所以 save,validate 函數(shù)不可能滿足每個(gè)人需要,不過可以設(shè)計(jì)一個(gè)模塊化表單,以便將來任何時(shí)候都能為其添加各種元素,不用修改 save 和 validate 函數(shù).
不用為表單元素的每一種可能組合編寫一套方法,應(yīng)該讓這兩個(gè)方法和表單域自身關(guān)聯(lián)起來.讓每個(gè)域都可以保存和驗(yàn)證自己:
nameFieldset.validate(); nameFieldset.save();
難點(diǎn)在于如何同時(shí)在所有域上執(zhí)行這些操作,使用組合模式來簡(jiǎn)化代碼:要保存所有域,只需一次調(diào)用:topForm.save();topForm 對(duì)象將會(huì)在所有子對(duì)象上遞歸調(diào)用 save 方法,實(shí)際的 save 操作只會(huì)發(fā)生在底層的葉對(duì)象上.組合對(duì)象志氣到了一個(gè)傳遞調(diào)用的作用.
實(shí)現(xiàn)代碼:
首先,創(chuàng)建組合對(duì)象和葉對(duì)象需要實(shí)現(xiàn)的兩個(gè)接口:
var Composite = new Interface("Composite", ["add", "remove", "getChild"]); var FormItem = new Interface("FormItem", ["save"]);
目前 FormItem 只要求實(shí)現(xiàn)一個(gè) save 函數(shù),稍后會(huì)對(duì)其進(jìn)行擴(kuò)充.
CompositeForm 類的代碼如下:
var CompositeForm = function(id, method, action) { // implements Composite, Formitem this.formComponents = []; this.element = document.createElement("form"); this.element.id = id; this.element.method = method || "POST"; this.element.action = action || "#"; }; CompositeForm.prototype.add = function (child) { Interface.ensureImplements(child, Composite, FormItem); this.formComponents.push(child); this.element.appendChild(child.getElement()); }; CompositeForm.prototype.remove = function (child) { for (var i = 0, len = this.formComponents.length; i < len; i++) { if (this.formComponents[i] === child) { this.formComponents.splice(i, 1); // Remove one element from the array at position i. break; } } }; CompositeForm.prototype.getChild = function (i) { return this.formComponents[i]; }; CompositeForm.prototype.remove = function (child) { for (var i = 0, len = this.formComponents.length; i < len; i++) { this.formComponents[i].save(); } }; CompositeForm.prototype.getElement = function () { return this.element; };
CompositeForm 的子對(duì)象保存在一個(gè)數(shù)組中.使用 Interface.ensureImplements 是為了保證要添加到組合對(duì)象中的對(duì)象實(shí)現(xiàn)了正確的接口.
現(xiàn)在看看葉對(duì)象類:
var Field = function (id) { // implements Composite, FormItem this.id = id; }; Field.prototype.add = function () {}; Field.prototype.remove = function () {}; Field.prototype.getChild = function () {}; Field.prototype.save = function () { setCookie(this.id, this.getValue()); }; Field.prototype.getElement = function () { return this.element; }; Field.prototype.getValue = function () { throw new Error("Unsupported operation on the class Field."); };
這個(gè)類將被各個(gè)葉對(duì)象類繼承.它將 Composite 接口中的方法實(shí)現(xiàn)為空函數(shù),因?yàn)槿~節(jié)點(diǎn)不會(huì)有子對(duì)象.
這里最簡(jiǎn)單地實(shí)現(xiàn)了 save 方法.但是把用戶原始數(shù)據(jù)放在 Cookie 是一個(gè)非常糟糕的做法.因?yàn)?Cookie 很容易被篡改,所以數(shù)據(jù)的有效性得不到保證.其次,存儲(chǔ)在 Cookie 中的數(shù)據(jù)有大小限制.所以用戶的數(shù)據(jù)可能不會(huì)被全部保存下來. 最后,還會(huì)影響性能,因?yàn)槊看握?qǐng)求中 Cookie 都會(huì)作為 http 頭被一起發(fā)送.
save 方法用 getValue 方法獲得所要保存的對(duì)象值,getValue() 方法各個(gè)子類中的實(shí)現(xiàn)各不相同.使用 save 方法,不用提交表單也能保存表單的內(nèi)容.
這個(gè)對(duì)于長(zhǎng)表單來說很有用,因?yàn)橛脩艨梢圆挥锰钔瓯韱沃型颈4? 然后忙完其他事情再來完成表單的填寫:
var InputField = function(id, label) { // implements Composite, FormItem Field.call(this, id); this.input = document.createElement("input"); this.input.id = id; this.label = document.createElement("label"); var labelTextNode = document.createTextNode(label); this.label.appendChild(lavelTExtNode); this.element = document.createElement("div"); this.element.className = "input-field"; this.label.appendChild(this.label); this.label.appendChild(this.input); }; extend(InputField, Field); // Inherit from Field. InputField.prototype.getValue = function () { return this.input.value; };
InputField 是 Field 的子類之一.大多數(shù)方法都是從Field 繼承而來.但是他也實(shí)現(xiàn)了針對(duì) input 標(biāo)簽的 getValue 方法的代碼.TextareaField 和 SelectField 也實(shí)現(xiàn)了自己特有的 getValue 方法.
var TextareaField = function(id, label) { // implements Composite, FormItem Field.call(this, id); this.textarea = document.createElement("textarea"); this.textarea.id = id; this.label = document.createElement("label"); var labelTextNode = document.createElement("select"); this.select.id = id; this.element = document.createElement("div"); this.element.className = "input-field"; this.element.appendChild(this.label); this.element.appendChild(this.select); }; extend(SelectField, Field); // Inherit from Field. SelectField.prototype.getValue = function () { return this.select.options[this.select.selectedIndex].value; };利
使用組合模式,簡(jiǎn)單的操作也能產(chǎn)生復(fù)雜的結(jié)果.不必編寫大量手工遍歷數(shù)據(jù)或者其他數(shù)據(jù)結(jié)構(gòu)的粘合代碼,只需對(duì)最頂層的對(duì)象執(zhí)行操作,讓每一個(gè)子對(duì)象自己傳遞這個(gè)操作.
組合模式中,各個(gè)對(duì)象之間的耦合非常松散.只要他們實(shí)現(xiàn)了同樣的接口,那么改變他們的位置或者互相交換很簡(jiǎn)單.促進(jìn)了代碼得寵用,也有利于代碼重構(gòu).
用組合模式組織起來的對(duì)象形成了一個(gè)出色的層次體系.每當(dāng)對(duì)頂層對(duì)象執(zhí)行一個(gè)操作時(shí),實(shí)際上是在對(duì)整個(gè)結(jié)構(gòu)進(jìn)行深度優(yōu)先搜索以查找節(jié)點(diǎn).在該層次體系中添加,刪除,查找節(jié)點(diǎn)都很容易.
組合對(duì)象的易用性可能掩蓋了它所支持的每一種操作的代價(jià).由于對(duì)組合對(duì)象調(diào)用的任何操作都會(huì)被傳遞到他的所有子對(duì)象,那么如果層次體系很大,系統(tǒng)的性能就會(huì)受到影響.topGallery.show()這樣一個(gè)方法的調(diào)用會(huì)引起一次對(duì)整個(gè)數(shù)結(jié)構(gòu)的遍歷.
小結(jié)它將一批子對(duì)象組織為樹形結(jié)構(gòu),只要一條命令就可以操作樹中的所有對(duì)象.他提高了代碼的模塊化程度,而且便于代碼重構(gòu)和對(duì)象的替換.這種模式特別適用于動(dòng)態(tài)的 HTML 用戶界面,在他的幫助下,可以在不知道用戶界面的最終格局的情況下進(jìn)行開發(fā).
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/80460.html