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

面向?qū)ο蟮腏avascript之二(接口實(shí)現(xiàn)介紹)

 更新時(shí)間:2012年01月27日 21:05:09   作者:  
接口是面向?qū)ο驤avascript工具箱中最有用的特性之一。我們都知道GOF在設(shè)計(jì)模式中說到:面向接口編程,而非面向?qū)崿F(xiàn)編程
就足以說明接口在面向?qū)ο蟮念I(lǐng)域中有多重要。但JS卻不像其他面向?qū)ο蟮母呒?jí)語言(C#,Java,C++等)擁有內(nèi)建的接口機(jī)制,以確定一組對(duì)象和另一組對(duì)象包含相似的的特性。所幸的是JS擁有強(qiáng)大的靈活性(我在上文已談過),這使得模仿接口特性又變得非常簡(jiǎn)單。那么到底是接口呢?

接口,為一些具有相似行為的類之間(可能為同一種類型,也可能為不同類型)提供統(tǒng)一的方法定義,使這些類之間能夠很好的實(shí)現(xiàn)通信。

那使用接口到底有哪些好處呢?簡(jiǎn)單地說,可提高系統(tǒng)相似模塊的重用性,使得不同類的通信更加穩(wěn)固。一旦實(shí)現(xiàn)接口,則必須實(shí)現(xiàn)接口中所有的方法。對(duì)于大型的Web項(xiàng)目來說,使得多個(gè)復(fù)雜模塊的相似功能模塊,只需要提供接口便可以提供一個(gè)實(shí)現(xiàn),而彼此之間不受到影響。但我們必須明確,接口也不是萬能的,由于JS是弱類型語言,你并不能強(qiáng)制其他的團(tuán)隊(duì)成員嚴(yán)格遵循你所提供的接口,不過你可以使用代碼規(guī)范和輔助類來緩解這個(gè)問題。另外,對(duì)系統(tǒng)性能也會(huì)造成一定的影響,應(yīng)根據(jù)你的系統(tǒng)需求復(fù)雜度而定。由于沒有內(nèi)建的interface和implements關(guān)鍵字,下面我們來看看JS是如何模仿實(shí)現(xiàn)接口的。

1. 最簡(jiǎn)單也是效果最差實(shí)現(xiàn)接口的方式是使用注釋。即在注釋中使用interface來說明接口的意圖。
復(fù)制代碼 代碼如下:

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
funtion save();
}
*/
var CompositeForm = function(id, name, action) {
// implements Composite, FormItem
}
CompositeForm.prototype = {
// implements Composite interface
add: function(child) {
//...
},
remove: function(child) {
//...
},
getChild: function(index) {
//...
}
// implements FormItem interface
save: function() {
//...
}
}

這并沒有很好的模擬接口的功能和確保Composite類確實(shí)實(shí)現(xiàn)了方法的集合,也沒有拋出錯(cuò)誤通知程序員問題所在,除了說明以外不起任何作用,所有的一致性都需要程序員自覺完成。但它容易實(shí)現(xiàn),不需要額外的類或函數(shù),不影響文檔的大小和執(zhí)行速度,注釋也能很輕易的剝離,在一定程度上提高了重用性,因?yàn)樘峁┝祟惖恼f明可以跟其他實(shí)現(xiàn)相同接口的類進(jìn)行通信。

2. 用屬性檢查模擬接口。類顯示聲明了要實(shí)現(xiàn)的接口,通過屬性檢查是否實(shí)現(xiàn)了相應(yīng)的接口。
復(fù)制代碼 代碼如下:

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
funtion save();
}
*/
var CompositeForm = function(id, name, action) {
this.implementsInterfaces = ["Composite", "FormItem"];
//...
}
function checkInterfaces(formInstance) {
if(!implements(formInstance, "Composite", "FormItem")) {
throw new Error("Object doesn't implement required interface.");
}
//...
}
//check to see if an instance object declares that it implements the required interface
function implements(instance) {
for(var i = 1; i < arguments.length; i++) {
var interfaceName = arguments[i];
var isFound = false;
for(var j = 0; j < instance.implementsInterfaces.length; j++) {
if(interfaceName == instance.implementsInterfaces[j]) {
isFound = true;
break;
}
}
if(!isFound) return false;// An interface was not found.
}
return true;// All interfaces were found.
}

在這里發(fā)現(xiàn),仍然添加了注釋來說明接口。不過在Composite類中添加了一個(gè)屬性implementsInterfaces,說明該類必須實(shí)現(xiàn)那些接口。通過檢查該屬性來判斷是否實(shí)現(xiàn)相應(yīng)的接口,如果未實(shí)現(xiàn)就會(huì)拋出錯(cuò)誤。但缺點(diǎn)在于你仍無法判斷是否真正實(shí)現(xiàn)了對(duì)應(yīng)的接口方法,僅僅只是"自稱"實(shí)現(xiàn)了接口,同時(shí)也增加了相應(yīng)的工作量。

3. 用"鴨式辨型"來實(shí)現(xiàn)接口。從屬性檢查實(shí)現(xiàn)中發(fā)現(xiàn)一個(gè)類是否支持所實(shí)現(xiàn)的接口無關(guān)緊要,只要接口中所有的方法出現(xiàn)在類中相應(yīng)的地方,那足以說明已經(jīng)實(shí)現(xiàn)接口了。就像"如果走路像鴨子,像鴨子嘎嘎的叫,不管它貼不貼標(biāo)簽說自己是鴨子,那我們認(rèn)為它就是鴨子"。利用輔助類來判斷一個(gè)類是否存在(實(shí)現(xiàn))了相應(yīng)接口中所有的方法,如果不存在則代表沒有實(shí)現(xiàn)。
復(fù)制代碼 代碼如下:

// Interfaces
var Composite = new Interface("Composite", ["add", "remove", "getChild"]);
var FormItem = new Interface("FormItem", ["save"]);

var CompositeForm = function(id, name, action) {
// implements Composite, FormItem interfaces
}
function checkInterfaces(formInstance) {
Interface.ensureImplements(formInstance, "Composite", "FormItem");
//...
}

接口類
復(fù)制代碼 代碼如下:

// Interface class is for checking if an instance object implements all methods of required interface
var Interface = function(name, methods) {
if(arguments.length != 2) {
throw new Error("Interface constructor expects 2 arguments, but exactly provided for " + arguments.length + " arguments.");
}
this.name = name;
this.methods = [];
for(var i = 0;i < methods.length; i++) {
if(typeof methods[i] != "string") {
throw new Error("Interface constructor expects to pass a string method name.");
}
this.methods.push(methods[i]);
}
}
//static class method
Interface.ensureImplements = function(instance) {
if(arguments.length < 2) {
throw new Error("Function Interface.ensureImplements expects at least 2 arguments, but exactly passed for " + arguments.length + " arguments.");
}
for(var i = 1, len = arguments.length; i < len; i++) {
var interface = arguments[i];
if(interface.constructor != Interface) {
throw new Error("Function Interface.ensureImplements expects at least 2 arguments to be instances of Interface.");
}
for(var j = 0, mLen = interface.methods.length; j < mLen; j++) {
var method = interface.methods[j];
if(!instance[method] || typeof instance[method] != "function") {
throw new Error("Function Interface.ensureImplements: object doesn't implements " + interface.name + ". Method " + method + " wasn't found.");
}
}
}
}

嚴(yán)格的類型檢查并非總是必需的,在平時(shí)的Web前端開發(fā)中很少用到以上的接口機(jī)制。但當(dāng)你面對(duì)一個(gè)復(fù)雜特別是擁有很多相似模塊的系統(tǒng)時(shí),面向接口編程將變得非常重要。看似降低了JS的靈活性,實(shí)質(zhì)上卻提高了類的靈活性,降低了類之間的耦合度,因?yàn)楫?dāng)你傳入任何一個(gè)實(shí)現(xiàn)了相同接口的對(duì)象都能被正確的解析。那什么時(shí)候使用接口比較合適呢?對(duì)于一個(gè)大型項(xiàng)目來說,肯定有許多團(tuán)隊(duì)成員,并且項(xiàng)目會(huì)被拆分為更細(xì)粒度的功能模塊,為了保證進(jìn)度需提前利用"占位程序"(接口)來說明模塊的功能或與已開發(fā)完成的模塊之間通信時(shí),提供一個(gè)統(tǒng)一的接口(API)顯得相當(dāng)必要。隨著項(xiàng)目的不斷推進(jìn),可能需求會(huì)不斷的發(fā)生變動(dòng),各模塊功能也會(huì)發(fā)生相應(yīng)的變動(dòng),但彼此之間通信以及提供給上層模塊的API始終保持不變,確保整個(gè)架構(gòu)的穩(wěn)定性和持久性。下面我們通過一個(gè)具體的示例來說明接口的實(shí)際應(yīng)用。假設(shè)設(shè)計(jì)一個(gè)類自動(dòng)檢測(cè)結(jié)果對(duì)象(TestResult類)并格式化輸出一個(gè)網(wǎng)頁視圖,沒有使用接口的實(shí)現(xiàn)方式:
復(fù)制代碼 代碼如下:

var ResultFormatter = function(resultObject) {
if(!(resultObject instanceof TestResult)) {
throw new Error("ResultFormatter constructor expects a instance of TestResult.");
}
this.resultObject = resultObject;
}
ResultFormatter.prototype.render = function() {
var date = this.resultObject.getDate();
var items = this.resultObject.getResults();
var container = document.createElement("div");
var header = document.createElement("h3");

header.innerHTML = "Test Result from " + date.toUTCString();
container.appendChild(header);
var list = document.createElement("ul");
container.appendChild(list);

for(var i = 0, len = items.length; i++) {
var item = document.createElement("li");
item.innerHTML = items[i];
list.appendChild(item);
}
return container;
}

首先ResultFormatter類的構(gòu)造函數(shù)僅僅是檢查了是否為TestResult實(shí)例,卻無法保證一定實(shí)現(xiàn)了render中的方法getDate()和getResults()。另外,隨著需求的不斷變動(dòng),現(xiàn)在有一個(gè)Weather類,包含了getDate()和getResults()方法,卻因?yàn)橹荒軝z查是否為TestResult的實(shí)例而無法運(yùn)行render方法,豈不是很無語呢?解決辦法是移除instanceof檢查并以接口代替。
復(fù)制代碼 代碼如下:

//create the ResultSet interface
var ResultSet = new Interface("ResultSet", ["getDate", "getResults"]);
var ResultFormatter = function(resultObject) {
// using Interface.ensureImplements to check the resultObject
Interface.ensureImplements(resultObject, ResultSet);
this.resultObject = resultObject;
}
ResultFormatter.prototype.render = function() {
// keep the same as former
var date = this.resultObject.getDate();
var items = this.resultObject.getResults();
var container = document.createElement("div");
var header = document.createElement("h3");

header.innerHTML = "Test Result from " + date.toUTCString();
container.appendChild(header);
var list = document.createElement("ul");
container.appendChild(list);

for(var i = 0, len = items.length; i++) {
var item = document.createElement("li");
item.innerHTML = items[i];
list.appendChild(item);
}
return container;
}

可以看出render方法沒有發(fā)生任何改變。改變的僅僅是添加一個(gè)接口和使用接口來進(jìn)行類型檢查。同時(shí)現(xiàn)在能夠傳遞Weather類的實(shí)例來進(jìn)行調(diào)用,當(dāng)然也能傳遞實(shí)現(xiàn)了ResultSet接口的任何類的實(shí)例,使檢查更加精確和寬容。隨著后續(xù)對(duì)JS設(shè)計(jì)模式的推出,接口會(huì)在工廠模式、組合模式、裝飾模式和命令模式中得到廣泛的應(yīng)用。希望大家可以細(xì)細(xì)品味接口給我們的JS模塊化設(shè)計(jì)帶來的益處。
  • Javascript面向?qū)ο缶幊蹋ǘ?構(gòu)造函數(shù)的繼承

    Javascript面向?qū)ο缶幊蹋ǘ?構(gòu)造函數(shù)的繼承

    這個(gè)系列的第一部分,主要介紹了如何"封裝"數(shù)據(jù)和方法,以及如何從原型對(duì)象生成實(shí)例。
    2011-08-08
  • javascript類式繼承新的嘗試

    javascript類式繼承新的嘗試

    研究javascript是很有意思的事情,以前我說過,在javascript中的繼承,在于維持prototype指向同一object就行了,確實(shí)這樣
    2012-01-01
  • JavaScript類和繼承 constructor屬性

    JavaScript類和繼承 constructor屬性

    本文介紹了JavaScript里面的constructor屬性。這個(gè)屬性是理解JavaScript類和繼承的重要基礎(chǔ)。
    2010-03-03
  • javascript 面向?qū)ο缶幊?function也是類

    javascript 面向?qū)ο缶幊?function也是類

    function在javascript中用來創(chuàng)建函數(shù)或方法,但要想實(shí)現(xiàn)面向?qū)ο蠓绞降木幊蹋愂遣豢苫蛉钡慕巧?,而且是主角?/div> 2009-09-09
  • javascript 面向?qū)ο笕吕砭氈屠^承

    javascript 面向?qū)ο笕吕砭氈屠^承

    利用原型繼承的關(guān)鍵有兩步操作,需要的朋友可以參考下。
    2009-12-12
  • javascript 寫類方式之八

    javascript 寫類方式之八

    這里用的是Ext core3.0,Ext中用Ext.extend來定義一個(gè)類(當(dāng)然它更多用來擴(kuò)展一個(gè)類),Ext整個(gè)框架各種控件如Panel,MessageBox等都是用Ext.extend方法來擴(kuò)展。這里僅僅用它來定義一個(gè)最簡(jiǎn)單的類。
    2009-07-07
  • JavaScript 創(chuàng)建對(duì)象和構(gòu)造類實(shí)現(xiàn)代碼

    JavaScript 創(chuàng)建對(duì)象和構(gòu)造類實(shí)現(xiàn)代碼

    JavaScript學(xué)習(xí)筆記:創(chuàng)建對(duì)象和構(gòu)造類.
    2009-07-07
  • Javascript 面向?qū)ο笾剌d

    Javascript 面向?qū)ο笾剌d

    在面向?qū)ο笳Z言里重載是很重要的一個(gè)特性,而JavaScript這個(gè)自稱面向?qū)ο蟮恼Z言竟然沒有直接提供重載的功能。
    2010-05-05
  • 最新評(píng)論