Ajax In Action 附錄B 2.6

B 2.6 接口和動態類型

在開發過程當中,我們有時候可能需要指定一些不需要具體實現的行爲,例如被正方形,圓形等形狀實現的shape對象,我們不能給他指定一個具體的形狀。這個shape對象就是一個抽象的東西,並沒有一個具體的實現。

一個C++的虛類,或者Java的接口就可以實現這種要求。我們經常會說接口定義了不同組件之間的約束,有了這些約束,這個shape就可以不用考慮具體的實現,而實現這個累得人也不用考慮其他庫文件的內容或者這個接口的其他實現。

接口提供了事務的分離,並且支持很多設計模式,如果我們在Ajax中要用設計模式,那麼我們就要用接口。JavaScript沒有關於接口的通用定義,那我們應該怎麼做呢?

最簡單的方法就是非正式的定義這些約束,然後依賴於使用接口的程序員來知道他們在幹什麼。Dave Thomas給這種方法起過一個名字“動態類型”,就是說如果我們像鴨子一樣走路,並且像鴨子一樣搖擺,那類似於shape接口的就是一個鴨子,如果我們能夠計算面積和周長的話,那就是一個形狀。

現在假設我們要將兩種shape的面積相加,用Java,我們可以這樣寫:

public double addAreas(Shape s1, Shape s2){

return s1.getArea()+s2.getArea();

}

這種寫法防止了我們將一個非shape作爲參數傳入,所以在方法體內,我們肯定是遵守了約束。但是在JavaScript中,方法的變量並沒有具體類型,所以也就沒有這種保護了:

function addAreas(s1,s2){

return s1.getArea()+s2.getArea();

}

如果這兩個對象當中的任意一個沒有getArea()這個函數,我們就會得到一個JavaScript錯誤,我們可以象下面這樣在調用之前先進行檢查:

function hasArea(obj){

return obj && obj.getArea && obj.getArea instanceof Function;

}

然後修改函數以便能用上這個檢查:

function addAreas(s1,s2){

var total=null;

if (hasArea(s1) && hasArea(s2)){

total=s1.getArea()+s2.getArea();

}

return total;

}

通過JavaScript的反射機制,我們可以寫一個函數用來檢查一個對象中是否有某個指定函數:

function implements(obj,funcName){

return obj && obj[funcName] && obj[funcName] instanceof Function;

}

或者,我們也可以把它增加到Object類的原型:

Object.prototype.implements=function(funcName){

return this && this[funcName] && this[funcName] instanceof Function;

}

這樣我們就可以通過名字來檢驗是否存在指定的函數:

function hasArea(obj){

return obj.implements("getArea");

}

或者直接通過接口來測試:

function isShape(obj){

return obj.implements("getArea") && obj.implements("getPerimeter");

}

這樣雖然不如Java中那樣安全,但是至少也安全了一些。可能有的對象要實現getArea()來返回一個字符型的返回值,而不是數字型,但是在JavaScript中如果不調用這個函數,我們是沒有辦法知道它的返回值類型的,因爲JavaScript沒有預定義的類型。不過可以寫一系列檢驗的函數,例如:

function isNum(arg){

return parseFloat(arg)!=NaN;

}

NaN是“not a number”的縮寫,一個用來處理數字格式錯誤的JavaScript變量,如果arg以數字開頭,這個函數就會返回true,實際上,parseFloat()和他的兄弟parseInt()會盡力提取可以認識的數字,比方說parseFloat(“64 hectares”)轉換成64,而不是NaN.

下面就進一步來完善addAreas()這個函數:

function addAreas(s1,s2){

var total=null;

if (hasArea(s1) && hasArea(s2)){

var a1=s1.getArea();

var a2=s2.getArea();

if (isNum(a1) && isNum(a2)){

total=parseFloat(a1)+parseFloat(a2);

}

}

return total;

}

在這裏,我調用了parseInt()來處理字符串型的變量。如果s1返回32s2返回64,那麼addAreas()將會返回96,如果沒有使用parseFloat的話,可能就會得到3264了。

總之,用動態類型可以很簡單,但是必須要在整個團隊內保持一致,在靈活的Ruby模式中,動態類型很受歡迎,如果一個人或者一個小團隊轉到一個大的工程,而且包括分組的話,這種類型的功能就要弱一些了,如果你想給動態類型家一些檢驗和平衡的話,這部分所講的就是你應該做的。

我們從對象的角度研究了語言,現在就從函數的角度來分析一下,看看他們到底是什麼。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章