欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

學(xué)習(xí)JavaScript設(shè)計(jì)模式(接口)

 更新時(shí)間:2015年11月26日 14:10:55   作者:小平果118  
這篇文章主要帶領(lǐng)大家學(xué)習(xí)JavaScript設(shè)計(jì)模式,其中重點(diǎn)介紹接口,舉例說明什么是接口,對接口進(jìn)行詳細(xì)剖析,感興趣的小伙伴們可以參考一下

1、接口概述

1)、什么是接口?

接口是提供了一種用以說明一個(gè)對象應(yīng)該具有哪些方法的手段。盡管它可以表明這些方法的語義,但它并不規(guī)定這些方法應(yīng)該如何實(shí)現(xiàn)。

2)、 接口之利

  • 促進(jìn)代碼的重用。

接口可以告訴程序員一個(gè)類實(shí)現(xiàn)了哪些方法,從而幫助其使用這個(gè)類。

  • 有助于穩(wěn)定不同類之前的通信方式。
  • 測試和調(diào)式因此也能變得更輕松。

在javascript這種弱類型語言中,類型不匹配錯(cuò)誤很難跟蹤。使用接口可以讓這種錯(cuò)誤的查找變午更容易一點(diǎn),因?yàn)榇藭r(shí)如果一個(gè)對象不像所要求的類型,或者沒有實(shí)現(xiàn)必要的方法,那么你會(huì)得到包含有用信息的明確的錯(cuò)誤提示。這樣一來,邏輯錯(cuò)誤可以被限制在方法自身,而不是在對象構(gòu)成之中。

  • 接口還能讓代碼變得更穩(wěn)固.

因?yàn)閷涌诘娜魏胃淖冊谒袑?shí)現(xiàn)它的類都必須體現(xiàn)出來。如果接口添加了一個(gè)操作,而某個(gè)實(shí)現(xiàn)它的類并沒有相應(yīng)的添加這個(gè)操作,那么你肯定會(huì)立即見到一個(gè)錯(cuò)誤。

3)、接口之弊

javascript是一種具有極強(qiáng)表現(xiàn)圖片的語言,這主要得益于其弱類型的特點(diǎn)。而接口的使用則一定程序上強(qiáng)化了類型的作用。這降低了語言的靈活性。javascript并沒有提供對接口的內(nèi)置支持,而試圖模仿其它語言內(nèi)置的功能總會(huì)有一些風(fēng)險(xiǎn)。

js中接口使用的最大問題在于,無法強(qiáng)迫其他程序員遵守你定義的接口。在其它語言中,接口的概念是內(nèi)置的,如果某人定義了實(shí)現(xiàn)一個(gè)接口的類,那么編譯器會(huì)確保該類的確實(shí)現(xiàn)了這個(gè)接口。而在javascript中則必須用手工的辦法保證某個(gè)類實(shí)現(xiàn)了一個(gè)接口。編碼規(guī)范和輔助類可以提供一些幫助,但無法徹底根除這個(gè)問題。如果項(xiàng)目的其他程序員不認(rèn)真對待接口,那么這些接口的使用是無法得到強(qiáng)制性保證的。除非項(xiàng)目的所有人都同意使用接口并對其進(jìn)行檢查,否則接口的很多價(jià)值都無從體現(xiàn)。

2、在javascript中模仿接口

javascript中模仿接口的三種方法:注解描述法、屬性檢查法、鴨式辨型法。

沒有哪種技術(shù)是完美的,但三者結(jié)合使用基本上可以令人滿意。

1)、注釋描述法實(shí)現(xiàn)接口

用注釋模仿接口是最簡單的方法,但效果卻是最差的。這種方法模仿其他頁面對象語言中的做法,使用了interface和implements關(guān)鍵字,但把它們放在注釋中,以免引起語法錯(cuò)誤。如下:

//javascript中定義接口的方式有三種:
//1、注解描述的方式

 /**  
  * interface Composite{
* function add(obj);
* function remove(obj);
* function update(obj);
}

優(yōu)點(diǎn):程序員可以有參考
缺點(diǎn):缺點(diǎn)一大堆,他只是一個(gè)借口的文檔范疇,假如不實(shí)現(xiàn)
   所有的方法,程序照樣可以運(yùn)行,太松散了。對測試和調(diào)試難度大
*/

// Implement of interface Composite
var CompositeImpl =function(){

 /*this.add = function(obj){

  };
  this.remove = function(obj){

  };
   這種函數(shù)定義的方法,在實(shí)例化一個(gè)對象的時(shí)候,new
   一個(gè)示例,將產(chǎn)生一個(gè)方法,且各個(gè)實(shí)力的方法還不一樣。
   所以采用下面的方法:
  */
  CompositeImpl.prototype.add = function(obj){

  }
  CompositeImpl.prototype.remove = function(obj){

  }    
  CompositeImpl.prototype.update = function(obj){
  }
}

var c1 = new CompositeImpl();
var c2 = new CompositeImpl()

alert(c1.add == c2.add)

這種模仿并不是很好。它沒有為確保Composite真正實(shí)現(xiàn)了正確的方法集而進(jìn)行檢查,也不會(huì)拋出錯(cuò)誤以告知程序員程序中的問題。說到底它主要還是屬于程序文檔范疇。在這種做法中,對接口約定的遵守完全依靠自覺。

2)、屬性檢測法實(shí)現(xiàn)接口

這種方法更嚴(yán)謹(jǐn)一點(diǎn)。所有類都明確地聲明自己實(shí)現(xiàn)了哪些接口,那些想與這些類打交道的對象可能針對這些聲明進(jìn)行檢查。那些接口自身仍然只是注釋,但現(xiàn)在你可以通過檢查一個(gè)屬性得知某個(gè)類自稱實(shí)現(xiàn)了什么接口。

/** 
  * interface Composite{
 *   function add(obj);
 *   function remove(obj);
 *   function update(obj);
 * }

 * interface FormItem{
 *   function select(obj);
 * }
 */

  // CompositeImpl implements interface Composite,FormItem
  var CompositeImpl =function(){
    //顯示在類的內(nèi)部,接收所實(shí)現(xiàn)的接口,一般來說,這是一個(gè)規(guī)范,
    // 我們項(xiàng)目經(jīng)理:在內(nèi)部類定義一個(gè)數(shù)組,名字要固定

    this.interfaceImplments = ['Composite','FormItem'];
    CompositeImpl.prototype.add = function(obj){
       alert("小平果");
  }
  CompositeImpl.prototype.remove = function(obj){

  }    
  CompositeImpl.prototype.update = function(obj){

  }
  /*CompositeImpl.prototype.select = function(obj){

  }*/
  }

  //定義函數(shù)檢測,判斷當(dāng)前對象是否實(shí)現(xiàn)了所有的接口
  function checkCompositeImpl (instance){
    if (!isImplments(instance,'Composite','FormItem')) {
      throw new Error('Object cannot implements all the interface');
    };
  }

  //公用的具體檢測方法(核心方法),主要目的就是判斷示例對象有沒有實(shí)現(xiàn)相關(guān)的接口;
  function isImplments(object){
    //arguments 對象會(huì)的函數(shù)的實(shí)際對象
    for (var i = 1, len = arguments.length; i < len; i++) { //注意這里從1開始,逐個(gè)方法判斷。
      var interfaceName = arguments[i];      //接收實(shí)現(xiàn)每一個(gè)接口的名字
      var interfaceFound = false;//判斷此方法到底是實(shí)現(xiàn)了還是失敗了?規(guī)范里定義了interfaceImplments.

      for (var j = 0;j < object.interfaceImplments.length; j++) {
        if(object.interfaceImplments[j] == interfaceName){
          interfaceFound = true;
          break;
        }
      };
       //如果沒有實(shí)現(xiàn),則返回false
       if (!interfaceFound) {
          return false;
       };

    }
     return true;
  }

var c1 = new CompositeImpl();
checkCompositeImpl(c1);

c1.add();

這個(gè)例子中,CompositeImpl 宣稱自己實(shí)現(xiàn)了Composite接口,其做法是把這兩個(gè)接口名稱加入一個(gè)名為implementsInterfaces的數(shù)組。類顯式聲明自己支持什么接口。任何一個(gè)要求基于參數(shù)屬于特定類型的函數(shù)都可以對這個(gè)屬性進(jìn)行檢查,并在所需接口未在聲明之列時(shí)拋出一個(gè)錯(cuò)誤。

這種方法有幾個(gè)優(yōu)點(diǎn)。它對類所實(shí)現(xiàn)的接口提供了文檔說明。如果需要的接口不在一個(gè)類宣稱支持的接口之列,你會(huì)看到錯(cuò)誤消息。通過利用這些錯(cuò)誤,你可以強(qiáng)迫其他程序員聲明這些接口。

這種方法的主要缺點(diǎn)在于它并未確保類真正實(shí)現(xiàn)了自稱實(shí)現(xiàn)的接口。你只知道它是否說自己實(shí)現(xiàn)了接口。在創(chuàng)建一個(gè)類時(shí)聲明它實(shí)現(xiàn)了一個(gè)接口,但后來在實(shí)現(xiàn)該接口所規(guī)定的方法時(shí)卻漏掉其中的某一個(gè),這種錯(cuò)誤很常見。此時(shí)所有檢查都能通過,但那個(gè)方法卻不存在,這將在代碼中埋下一個(gè)隱患。另外顯式聲明類所支持的接口也需要一些額外的工作。

3)、鴨式辨型法實(shí)現(xiàn)接口

其實(shí),類是否聲明自己支持哪些接口并不重要,只要它具有這些接口中的方法就行。鴨式辨型(這個(gè)名稱來自James Whitomb Riley的名言:“像鴨子一樣走路并且嘎嘎叫的就是鴨子”)正是基于這樣的認(rèn)識(shí)。它把對象實(shí)現(xiàn)的方法集作作為判斷它是不是某個(gè)類的實(shí)例的唯一標(biāo)準(zhǔn)。這種技術(shù)在檢查一個(gè)類是否實(shí)現(xiàn)了某個(gè)接口時(shí)也可大顯向身手。這種方法背后的觀點(diǎn)很簡單:如果對象具有與接口定義的方法同名的所有方法,那么就可以認(rèn)為它實(shí)現(xiàn)了這個(gè)接口。你可以用一個(gè)輔助函數(shù)來確保對象具有所有必需的方法:

/* 實(shí)現(xiàn)接口的第三種方式:鴨式辨型發(fā)實(shí)現(xiàn)接口,(較為完美的實(shí)現(xiàn)方法)
   核心思想:一個(gè)類實(shí)現(xiàn)接口的主要目的:把其中的方法都實(shí)現(xiàn)了(檢測方法)
   完全面向?qū)ο?代碼實(shí)現(xiàn)統(tǒng)一,實(shí)現(xiàn)解耦*/

//1、接口類---Class Interface ===>實(shí)例化N多個(gè)接口

/**
 *接口類的參數(shù)?幾個(gè)
 * 參數(shù)1:接口名
 * 參數(shù)2:接收方法的集合(數(shù)組)
 */
var Interface = function(name , methods){
   //判斷接口的參數(shù)個(gè)數(shù)
   if (arguments.length !=2) {
     throw new Error('the instance interface constructor arguments should be 2');
   };
   this.name =name;
   //this.methods = methods;
   this.methods = [];
   for (var i = 0, len = methods.length; i <len; i++) {
     if (typeof methods[i] !== "string"){
        throw new Error('the name of method is wrong');
     }
     this.methods.push(methods[i]);
   } 
}

//2、準(zhǔn)備工作,具體的實(shí)現(xiàn)

//(1)實(shí)例化接口對象
var CompositeInterface = new Interface('CompositeInterface',['add','delete']);
var FormItemInterface = new Interface('FormItemInterface',['update','select']);


 //(2)具體的實(shí)現(xiàn)類
//CompositeImpl implments CompositionIterface FormItemIterface
var CompositeImpl = function(){

}


//(3)實(shí)現(xiàn)接口的方法 implements methods
CompositeImpl.prototype.add = function(obj){
  alert("add");
}
CompositeImpl.prototype.delete = function(obj){
  alert("delete");
}     
CompositeImpl.prototype.update = function(obj){
  alert("update");
}
/*CompositeImpl.prototype.select = function(obj){
  alert("select");
}*/


//3、檢驗(yàn)接口里的方法
//如果檢測通過,不做任何操作;不通過,則拋出異常。
//這個(gè)方法的目的就是 檢測方法的

Interface.ensureImplements =function(object){
   //如果接受參數(shù)長度小于2 ,證明還有任何實(shí)現(xiàn)的接口
   if (arguments.length < 2) {
     throw new Error('The Interface has no implement class');
   };

   //獲得接口的實(shí)例對象
  for (var i = 1, len= arguments.length; i < len; i++) {
     var instanceInterface =arguments[i];
     //判斷參數(shù)是否為 接口類的類型
     if (instanceInterface.constructor !==Interface) {
        throw new Error('The arguments constructor is not Interface Class');
     };

     for (var j = 0, len2 =instanceInterface.methods.length ; j <len2; j++ ) {
        //用一個(gè)臨時(shí)變量 ,接收每個(gè)方法的名字(注意為字符串類型)
        var methodName = instanceInterface.methods[j];
        //object[key] 獲得方法
        if (!object[methodName] || typeof object[methodName] !== 'function')
        {
          throw new Error('the method"'+ methodName+'"is not found');
        }
     }
   }
}

var c1 =new CompositeImpl();
Interface.ensureImplements(c1,CompositeInterface,FormItemInterface);
c1.add();

與另外兩種方法不同,這種方法并不借助注釋。其各個(gè)方面都是可以強(qiáng)制實(shí)施的。ensureImplements函數(shù)需要至少兩個(gè)參數(shù)。第一個(gè)參數(shù)是想要檢查的對象。其余參數(shù)是據(jù)以對那個(gè)對象進(jìn)行檢查的接口。該函數(shù)檢查其第一個(gè)參數(shù)代表的對象是否實(shí)現(xiàn)了那些接口所聲明的所有方法。如果發(fā)現(xiàn)漏掉了任何一個(gè)方法,它就會(huì)拋出錯(cuò)誤,其中包含了所缺少的那個(gè)方法和未被正確實(shí)現(xiàn)的接口的名稱等有用信息。這種檢查可以用在代碼中任何需要確保某個(gè)對象實(shí)現(xiàn)了某個(gè)接口的地方。在本例中,addForm函數(shù)僅當(dāng)一個(gè)表單對象支持所有必要的方法時(shí)才會(huì)對其執(zhí)行添加操作。

盡管鴨式辨型可能是上述三種方法中最有用的一種,但它也有一些缺點(diǎn)。這種方法中,類并不聲明自己實(shí)現(xiàn)了哪些接口,這降低了代碼的可重用性,并且也缺乏其他兩種方法那樣的自我描述性。它需要使用一個(gè)輔助類Interface和一個(gè)輔助函數(shù)ensureImplements。而且,它只關(guān)心方法的名稱,并不檢查其參數(shù)的名稱、數(shù)目或類型。

3、Interface類的使用場合

嚴(yán)格的類型檢查并不總是明智的。許多js程序員根本不用接口或它所提供的那種檢查,也照樣一干多年。接口在運(yùn)用設(shè)計(jì)模式實(shí)現(xiàn)復(fù)雜系統(tǒng)的時(shí)候最能體現(xiàn)其價(jià)值。它看似降低javascript的靈活性,而實(shí)際上,因?yàn)槭褂媒涌诳梢越档蛯ο箝g的耦合程度,所以它提高了代碼的靈活性。接口可以讓函數(shù)變得更靈活,因?yàn)槟慵饶芟蚝瘮?shù)傳遞任何類型的參數(shù),又能保證它只會(huì)使用那些具有必要方法的對象。

4、Interface類的用法

判斷代碼中使用接口是否劃算是最重要的一步。對于小型的、不太費(fèi)事的項(xiàng)目來說,接口的好處也許并不明顯,只是徒增其復(fù)雜度而已。你需要自行權(quán)衡其利弊。如果認(rèn)為在項(xiàng)目中使用接口利大于弊,那么可以參照如下使用說明:
1)、 將Interface類納入HTML文件。
2)、 逐一檢查代碼中所有以對象為參數(shù)的方法。搞清代碼正常運(yùn)轉(zhuǎn)要求的這些對象參數(shù)具有哪些方法
3)、 為你需要的每一個(gè)不同的方法集創(chuàng)建一個(gè)Interface對象。
4)、 剔除所有針對構(gòu)造器顯式檢查。因?yàn)槲覀兪褂檬区喪奖嫘停詫ο蟮念愋筒辉僦匾?
5)、 以Interface.ensureImplements取代原來的構(gòu)造器檢查。

示例
假設(shè)你要?jiǎng)?chuàng)建一個(gè)類,它可以將一些自動(dòng)化測試結(jié)果轉(zhuǎn)化為適于在網(wǎng)頁上查看的格式。該類的構(gòu)造器以一個(gè)TestResult類的實(shí)例為參數(shù)。它會(huì)應(yīng)客戶的請求對這個(gè)TestResult對象所封裝的數(shù)據(jù)進(jìn)行格式化,然后輸出。
原始定義:

 var ResultFormatter =function(resultsObject){
    if(!(resultsObject instanceof TestResult)){
      throw newError("ResultsFormatter:constructor requires an instance of TestResult asan argument.")
    }
    this.resultsObject = resultsObject;
  }
  ResultFormatter.prototype.renderResults =function(){
    var dateOfTest = this.resultsObject.getDate();
    var resultsArray =this.resultsObject.getResults();
    var resultsContainer =document.createElement('div');
    var resultsHeader =document.createElement("h3");
    resultsHeader.innerHTML = "TestResults from "+dateOfTest.toUTCString();
    resultsContainer.appendChild(resultsHeader);
    var resultList =document.createElement("ul");
    resultsContainer.appendChild(resultList);
    for(var i=0,len=resultsArray.length;i<len;i++){
      var listItem=document.createElement('li');
      listItem.innerHTML =resultsArray[i];
      resultList.appendChild(listItem);
    }
    return resultsContainer;
  }

該類的構(gòu)造器會(huì)對參數(shù)進(jìn)行檢查,以確保其的確為TestResult類的實(shí)例。如果參數(shù)達(dá)不到要示,構(gòu)造器將拋出一個(gè)錯(cuò)誤。有了這樣的保證,在編寫renderResults方法時(shí),你就可以認(rèn)定有g(shù)etDate和getResults這兩個(gè)方法可供使用。實(shí)際上這并不能保證所需要的方法得到了實(shí)現(xiàn)。TestResult類可能會(huì)被修改,致使其不再擁有g(shù)etDate()方法。在此情況下,構(gòu)造器中的檢查仍能通過,但renderResults方法卻會(huì)失靈。

此外,構(gòu)造器的這個(gè)檢查施加了一些不必要的限制。它不允許使用其他類的實(shí)例作為參數(shù),哪怕它們原本可以如愿發(fā)揮作用。例如,有一個(gè)名為WeatherData在也擁有g(shù)etDate和getResults這兩個(gè)方法。它本來可以被ResultFormatter類用得好好的。但是那個(gè)顯式類型檢查會(huì)阻止使用WeatherData類的任何實(shí)例。
問題解決辦法是刪除那個(gè)使用instanceOf的檢查,并用接口代替它。首先,我們需要?jiǎng)?chuàng)建這個(gè)接口:

//ResultSetInterface.
var ResultSet =new Interface(“ResultSet”,[‘getDate','getResults']);

上面的這行代碼創(chuàng)建了一個(gè)Interface對象的新實(shí)例。第一個(gè)參數(shù)是接口的名稱,第二個(gè)參數(shù)是一個(gè)字符串?dāng)?shù)組,其中的每個(gè)字符串都是一個(gè)必需的方法名稱。有了這個(gè)接口之后,就可以用接口檢查替代instanceOf檢查了

var ResultFormatter = function(resultsObject){
 Interface.ensureImplements(resultsObject,ResultSet);
 this.resultsObject = resultsObject;
}
ResultFormatter.prototype.renderResults= function(){
 …
}

renderResults方法保持不變。而構(gòu)造器則被改為使用ensureImplements方法而不是instanceof運(yùn)算符。現(xiàn)在構(gòu)造器可以接受WeatherData或其他任何實(shí)現(xiàn)所需要方法的類的實(shí)例。我們只修改了幾行ResultFormatter類代碼,就讓那個(gè)檢查變得更準(zhǔn)確,而且更寬容。

5、依賴于接口的設(shè)計(jì)模式

  • 工廠模式
  • 組合模式
  • 裝飾模式
  • 命令模式

以上就是JavaScript設(shè)計(jì)模式中接口的實(shí)現(xiàn)相關(guān)介紹,希望對大家的學(xué)習(xí)有所幫助。

相關(guān)文章

最新評(píng)論