標籤:fonts new var string abs 添加 種類 lan ref
設計模式
設計模式是命名、抽象和識別對可重用的物件導向設計實用的的通用設計結構。
設計模式確定類和他們的實體、他們的角色和協作、還有他們的責任分配。
每個設計模式都聚焦於一個物件導向的設計難題或問題。
它描寫敘述了在其他設計的約束下它是否能使用。使用它後的後果和得失。
由於我們必須終於實現我們的設計模式,所以每個設計模式都提供了範例,代碼來對實現進行闡釋.
儘管設計模式被描寫敘述為物件導向的設計,它們基於那些已經被主流物件導向語言實現過的解決方式...”。
種類
設計模式能夠被分成幾個不同的種類。
在這個部分我們將分為三類:建立型設計模式、結構設計模式、行為設計模式。
建立型設計模式
建立型設計模式關注於對象建立的機制方法,通過該方法,對象以適應工作環境的方式被建立。主要的對象建立方法可能會給項目添加額外的複雜性,而這些模式的目的就是為了通過控制建立過程解決問題。
屬於這一類的一些模式是:構造器模式(Constructor),原廠模式(Factory),抽象原廠模式(Abstract),原型模式(Prototype),單例模式(Singleton)以及 建造者模式(Builder)。
結構設計模式
結構模式關注於對象組成和通常識別的方式實現不同對象之間的關係。該模式有助於在系統的某一部分發生改變的時候,整個系統結構不須要改變。該模式相同有助於對系統中某部分沒有達到某一目的的部分進行重組。
在該分類下的模式有:裝飾模式,面板模式,享元模式。適配器模式和代理模式。
行為設計模式
行為模式關注改善或精簡在系統中不同對象間通訊。
行為模式包含:迭代模式。中介者模式,觀察者模式和訪問者模式。
以下我們通過分開介紹各個經常使用的設計模式,來加深對設計模式的理解。
構造器模式
構造器是一個當建立對象的記憶體被分配後,用來初始化該對象的一個特殊函數。物件建構器是被用來建立特殊類型的對象的,首先它要準備使用的對象。其次在對象初次被建立時,通過接收參數。構造器要用來對成員的屬性和方法進行賦值。
因為javascript不支援類的概念,所以必須通過構造器來使用newkeyword初始化對象。
一個基礎的構造器代碼例如以下:
function Car( model, year, miles ) { this.model = model; this.year = year; this.miles = miles; this.toString = function () { return this.model + " has done " + this.miles + " miles"; };} // 使用: // 我們能夠示範範例化一個Carvar civic = new Car( "Honda Civic", 2009, 20000 );var mondeo = new Car( "Ford Mondeo", 2010, 5000 ); // 開啟瀏覽器控制台查看這些對象toString()方法的輸出值// output of the toString() method being called on// these objectsconsole.log( civic.toString() );console.log( mondeo.toString() );
可是這種話,繼承起來比較麻煩,並且每一個Car建構函式建立的對象中,toString之類的函數都會被又一次定義。所以還是要利用原型,來實現最佳的構造器:
function Car( model, year, miles ) { this.model = model; this.year = year; this.miles = miles; } // 注意這裡我們使用Note here that we are using Object.prototype.newMethod 而不是// Object.prototype 。以避免我們又一次定義原型對象Car.prototype.toString = function () { return this.model + " has done " + this.miles + " miles";}; // 使用: var civic = new Car( "Honda Civic", 2009, 20000 );var mondeo = new Car( "Ford Mondeo", 2010, 5000 ); console.log( civic.toString() );console.log( mondeo.toString() );
原廠模式
一個工廠能提供一個建立對象的公用介面。我們能夠在當中指定我們希望被建立的工廠對象的類型。說的簡單點。就像飲水機,要咖啡還是牛奶取決於你按哪個button。
簡單原廠模式在建立ajax對象的時候能夠體現出來,能夠通過jquery中的$.ajax方法來理解,也能夠通過我自己寫的一個ajax方法來理解。地址在:http://runjs.cn/code/j5dkikwu
ajax("test002.txt",{type:"GET",data:{name:"liuf",age:23},onsuccess:function(responseText,xhr){document.getElementById("input").value=responseText;},onfail:function(){//document.write("fail");}});
ajax實際上就是一個Factory 方法,至於究竟是用get方法還是post方法,都由後面的代碼來決定。這就是前面所說的“一個工廠能提供一個建立對象的公用介面,我們能夠在當中指定我們希望被建立的工廠對象的類型”。
單例模式
單例模式之所以這麼叫。是由於它限制一個類僅僅能有一個執行個體化對象。經典的實現方式是。建立一個類,這個類包括一個方法,這種方法在沒有對象存在的情況下,將會建立一個新的執行個體對象。
假設對象存在,這種方法僅僅是返回這個對象的引用。可是javascript本來就是無類的,所以簡單地來說。就是沒有就建立,有就不建立直接用。
那麼我們看看現實中的案例吧。點擊一個button後出現一個遮罩層,這是一個經常使用的需求吧。代碼例如以下:
var createMask = function(){ return document,body.appendChild( document.createElement(div) ); }$( ‘button‘ ).click( function(){ var mask = createMask(); mask.show(); })
這樣寫就會出現一個問題,就是每次調用createMask都會建立一個新的div,儘管能夠在隱藏遮罩層時將其remove,可是這樣還是會帶來效能的損耗。那麼能夠做例如以下改進。就是在頁面一開始就建立div。代碼例如以下:
var mask = document.body.appendChild( document.createElement( ‘‘div‘ ) ); $( ‘‘button‘ ).click( function(){ mask.show(); } )
這樣確實能夠保證頁面僅僅會建立一個遮罩層,可是也有一個問題,就是假設使用者不須要用到這個div,豈不是白白建立了它。於是我們能夠藉助一個變數來推斷是否已經建立過div,代碼例如以下:
var mask; var createMask = function(){ if ( mask ) return mask; else{ mask = document,body.appendChild( document.createElement(div) ); return mask; } }
這樣看起來不錯,可是mask作為一個全域變數。是否會造成汙染呢?所以最好的辦法例如以下:
var createMask = function(){ var mask; return function(){ return mask || ( mask = document.body.appendChild( document.createElement(‘div‘) ) ) }}()
這就是前面所說的“沒有就建立,有就不建立直接用”。沒錯,單例模式就是這麼簡單,設計模式事實上並不難。編程中我們事實上一直實用到,僅僅是自己沒有發現罷了。
橋接模式
橋接模式就是將實現部分和抽象部分分離開來。以便兩者能夠獨立的變化。在實現api的時候,橋接模式很經常使用。
我們以javascript的forEach方法為例:
forEach = function( ary, fn ){ for ( var i = 0, l = ary.length; i < l; i++ ){ var c = ary[ i ]; if ( fn.call( c, i, c ) === false ){ return false; } }}
能夠看到,forEach函數並不關心fn裡面的詳細實現. fn裡面的邏輯也不會被forEach函數的改寫影響.
使用代碼例如以下:
forEach( [1,2,3], function( i, n ){ alert ( n*2 ) } ) forEach( [1,2,3], function( i, n ){ alert ( n*3 ) } )
面板模式
面板模式是一種無處不在的模式,面板模式提供一個高層介面,這個介面使得client或者子系統調用起來更加方法。
比方:
var getName = function(){ return ‘‘svenzeng"}var getSex = function(){ return ‘man‘}
如今我要調用兩個方法,我就能夠使用一個更加高層的介面來調用:
var getUserInfo = function(){ var info = getName () + getSex (); return info;}
這樣就方便組裝,假設一開始就把兩個寫到一個函數中,那就不可以僅僅單獨調用當中一個了。
享元模式
享元模式是一個最佳化反覆、緩慢和低效資料共用代碼的經典結構化解決方式。它的目標是以相關對象儘可能多的共用資料,來降低應用程式中記憶體的使用(比如:應用程式的配置、狀態等)。通俗的講,享元模式就是用來降低程式所需的對象個數。
舉一個範例,網頁中的瀑布流。或者webqq的好友名單中。每次往下拉時。都會建立新的div。那麼假設有非常對div呢?瀏覽器豈不是卡死了?所以我們會想到一種辦法。就是把已經消失在視線外的div都刪除掉。這樣頁面就能夠保持一定數量的節點,可是頻繁的刪除和加入節點,又會帶來非常大的效能開銷。
這個時候就能夠用到享元模式了,享元模式能夠提供一些共用的對象以便反覆利用。比方頁面中僅僅能顯示10個div,那始終出如今使用者視線中的這10個div就能夠寫成享元。
原理事實上非常easy, 把剛隱藏起來的div放到一個數組中, 當須要div的時候, 先從該數組中取, 假設數組中已經沒有了, 再又一次建立一個. 這個數組裡的div就是享元, 它們每個都能夠當作不論什麼使用者資訊的載體.代碼例如以下:
var getDiv = (function(){ var created = []; var create = function(){ return document.body.appendChild( document.createElement( ‘div‘ ) ); } var get = function(){ if ( created.length ){ return created.shift(); }else{ return create(); } }/* 一個如果的事件,用來監聽剛消失在視線外的div,實際上能夠通過監聽滾動欄位置來實現 */ userInfoContainer.disappear(function( div ){ created.push( div ); }) })() var div = getDiv(); div.innerHTML = "${userinfo}";
適配器模式
適配器模式就是將一個類的介面轉換成客戶希望的另外一個介面。通俗一點的說。將像蘋果手機不能差在電腦機箱上,必須有一個轉換器。而這個轉換器就是適配器。
在程式裡適配器模式也經經常使用來適配2個介面, 比方你如今正在用一個自己定義的js庫. 裡面有個依據id擷取節點的方法$id(). 有天你認為jquery裡的$實現得更酷, 但你又不想讓你的project師去學習新的庫和文法. 那一個適配器就能讓你完畢這件事情.
$id = function( id ){ return jQuery( ‘#‘ + id )[0]; }
這樣就不用再一個一個的改動了。
代理模式
代理模式就是把對一個對象的訪問,交給還有一個代理對象來操作。
說得通俗一點,程式猿每天寫日報。日報最後會給總監批閱。可是假設全部人都直接發給總監。那總監就沒法工作了。
所以每一個人會把自己的日報發給自己的組長,再由組長轉寄給總監。這個組長就是代理。
編程中用到代理模式的情況也不少,比方大量操作dom時,我們會先建立文檔片段,再統一加到dom樹中。
示比例如以下:
中介者模式
中介者模式是觀察者模式中的共用被觀察者對象。
在這個系統中的對象之間直接的公布/訂閱關係被犧牲掉了。取而代之的是維護一個通訊的中心節點。中介者模式和代理模式是有差別的。差別例如以下:
中介者對象能夠讓各個對象之間不須要相互引用。從而使其耦合鬆散,並且能夠獨立的改變透明之間的互動。
通俗點講,銀行在存款人和貸款人之間也能看成一個中介。存款人A並不關心他的錢最後被誰借走。
貸款人B也不關心他借來的錢來自誰的存款。由於有中介的存在。這場交易才變得如此方便。
在編程中,大名鼎鼎的MVC結構中的Controller不就是一個中介者嗎?拿backbone舉例. 一個mode裡的資料並不確定最後被哪些view使用. view須要的資料也能夠來自隨意一個mode. 全部的綁定關係都是在controler裡決定. 中介者把複雜的多對多關係, 變成了2個相對簡單的1對多關係。
示範範例代碼例如以下:
var mode1 = Mode.create(), mode2 = Mode.create();var view1 = View.create(), view2 = View.create();var controler1 = Controler.create( mode1, view1, function(){ view1.el.find( ‘‘div‘ ).bind( ‘‘click‘, function(){ this.innerHTML = mode1.find( ‘data‘ ); } )})var controler2 = Controler.create( mode2 view2, function(){ view1.el.find( ‘‘div‘ ).bind( ‘‘click‘, function(){ this.innerHTML = mode2.find( ‘data‘ ); } )})觀察者模式
觀察者模式是這樣一種設計模式。
一個被稱作被觀察者的對象,維護一組被稱為觀察者的對象,這些對象依賴於被觀察者,被觀察者自己主動將自身的狀態的不論什麼變化通知給它們。
當一個被觀察者須要將一些變化通知給觀察者的時候,它將採用廣播的方式,這條廣播可能包括特定於這條通知的一些資料。
當特定的觀察者不再須要接受來自於它所注冊的被觀察者的通知的時候,被觀察者能夠將其從所維護的組中刪除。
通俗點理解,就是面試官是被觀察者。而等待通知的人是觀察者。
javascript中平時接觸的dom事件。事實上就是一種觀察者模式的體現:
div.onclick = function click (){ alert ( ‘‘click‘ ) }
僅僅要訂閱了div的click事件,當點擊div的時候,click函數就會運行。
觀察者模式能夠非常好的實現連個模組之間的解耦,假如一個多人合作的項目,我負責Map和Gamer模式:
loadImage( imgAry, function(){ Map.init(); Gamer.init(); } )
<p>而別人負責loadImage方法的維護。假設有一天,我要加上一個Sound模組,而我無權動用別人的代碼。僅僅能空空等待別人回來加入:</p><pre name="code" class="html">loadImage( imgAry, function(){ Map.init(); Gamer.init(); Sount.init(); } )
所以我們能夠做例如以下修改:
loadImage.listen( ‘‘ready‘, function(){ Map.init(); }) loadImage.listen( ‘‘ready‘, function(){ Gamer.init(); }) loadImage.listen( ‘‘ready‘, function(){ Sount.init(); })
loadImage完畢之後。不用再關心未來會發送什麼,接下來它僅僅須要發送一個訊號:
loadImage.trigger( ‘ready’ );
全部監聽ready事件的對象就都會收到通知。這就是觀察者模式的應用情境。
講到這裡。經常使用的設計模式也講完了,深入理解javascript系列也將告一段落。
深入理解javascript之設計模式