什么是接口
接口提供了一種用以說(shuō)明一個(gè)對(duì)象應(yīng)該具有哪些方法的手段喊儡。
接口之利
- 接口可以告訴一個(gè)程序員一個(gè)類實(shí)現(xiàn)了哪些方法挤渔,從而幫助其使用這個(gè)類阿逃。
- 接口還有助于穩(wěn)定不同類之間的通信方式逸绎。
接口之弊
- 接口的使用在一定程度上強(qiáng)化了類型的作用骡男,這降低了語(yǔ)言的靈活性淆游。
- JS并沒(méi)有提供對(duì)接口的內(nèi)置支持,而試圖模仿其他語(yǔ)言的內(nèi)置功能總是會(huì)有一些風(fēng)險(xiǎn)
- JS中任何實(shí)現(xiàn)接口的方法都會(huì)對(duì)性能造成一定影響隔盛,在某種程度上這得歸咎于額外的方法調(diào)用的開(kāi)銷
- 接口的使用最大的問(wèn)題在于犹菱,無(wú)法強(qiáng)迫其他程序員遵守你定義的接口
模仿接口實(shí)現(xiàn)
- 用注釋描述接口
/*
interface Composite {
function add(child);
function remove(child);
function getChild(child);
}
interface FormItem {
function save(child);
}
*/
var CompositeFrom = function(id,method,action){
...
};
CompositeFrom .prototype.add = function(child){
...
};
CompositeFrom .prototype.remove= function(child){
...
};
CompositeFrom .prototype.getChild= function(child){
...
};
CompositeFrom .prototype.save= function(child){
...
};
這種做法易于實(shí)現(xiàn),不需要額外的類或函數(shù)吮炕±巴眩可以提高代碼的可重用性。
它的缺點(diǎn)在于沒(méi)有為確保CompositeFrom真正實(shí)現(xiàn)了的正確方法而進(jìn)行檢查龙亲,也不會(huì)拋出錯(cuò)誤告訴程序員程序中有問(wèn)題陕凹,所以對(duì)于測(cè)試和調(diào)試也沒(méi)有什么幫助。
- 屬性檢查模仿接口
/*
interface Composite {
function add(child);
function remove(child);
function getChild(child);
}
interface FormItem {
function save(child);
}
*/
var CompositeFrom = function(id,method,action){
//定義一個(gè)數(shù)組 存放將要實(shí)現(xiàn)的接口
this.implementsInterface = ['Composite','FormItem'];
...
};
function addForm(formInstance){
// 調(diào)用檢查函數(shù) 如果存在未定義的接口 拋出錯(cuò)誤
if(!implements(formInstance,'Composite','FormItem')){
throw new Error('Object does not implement a required interface')
}
...
}
function implements(object){
for (var i = 1;i<argumements.length;i++){
// 遍歷參數(shù) 跳過(guò)第一個(gè)
var interfaceName = argument[i]; //接口名稱
var interfaceFound = false ; //flag
for (var j=0;j<object.implementsInterface.length;j++){
// 遍歷存放接口名稱的數(shù)組
if(implementsInterface[j] == interfaceName){
interfaceFound = true;
break;
}
}
// 如果找到返回true 否則返回false
return interfaceFound
}
}
這種方法不僅對(duì)所實(shí)現(xiàn)的接口進(jìn)行了注釋說(shuō)明鳄炉,如果需要的接口不在一個(gè)類所宣稱支持接口之內(nèi)杜耙,它還會(huì)拋出一個(gè)錯(cuò)誤,這樣就可以強(qiáng)迫其它程序員聲明這些接口迎膜。
它的缺點(diǎn)在于你不能保證所聲明的接口是否真正實(shí)現(xiàn)泥技。所以會(huì)存在檢查通過(guò)浆兰,而方法不存在的問(wèn)題磕仅。
- 鴨式辨型模仿接口
// Interface
var Composite = new Interface('Composite',['add','remove','getChild']);
var FormItem= new Interface('FormItem',['save']);
//CompositeForm 類
var CompositeForm= function(id,method,action){
...
};
functiom addForm(formIntance){
// 輔助函數(shù) 如果引入未定義的接口會(huì)拋出錯(cuò)誤
ensureImplements(formIntance,Composite,FormItem);
...
}
這種方法不依賴于注釋珊豹。其各個(gè)方面都是可以強(qiáng)制實(shí)施的。ensureImplements函數(shù)至少需要兩個(gè)參數(shù)榕订,第一個(gè)參數(shù)是想要檢查的對(duì)象店茶,其余參數(shù)是據(jù)以對(duì)那個(gè)對(duì)象進(jìn)行檢查的接口。
它的缺點(diǎn)在于劫恒,類并不聲明自己實(shí)現(xiàn)了哪些接口贩幻,降低了代碼的可重用性,也缺乏上面兩種方法的自我描述性两嘴。它依賴于輔助類Interface和輔助函數(shù)ensureImplements丛楚。
Interface類的實(shí)現(xiàn)
// 構(gòu)造函數(shù)
var Interface = function(name,methods){
if(arguments.length != 2){
throw new Error(`Interface constructor called with ${arguments.length} arguments, but expcted 2.`);
}
this.name = name;
this.methods = [];
for(var i =0;i<methods.length;i++){
if(typeof methodsp[i] !== 'string'){
throw new Error("Interface constructor expects method name to be passed in as a string");
}
this.methods.push(methods[i]);
}
};
// 輔助函數(shù)
Interface.ensureImplements = function(object){
// 如果參數(shù)小于2個(gè)拋出錯(cuò)誤
if(arguments.length<2){
throw new Error(`Function Interface.ensureImplements called with ${arguments.length} arguments, but expected at least 2.`);
}
for(var i = 1; i<arguments.length; i++){
var interface = arguments[i];
// 如果類的構(gòu)造函數(shù)不是Interface 拋出錯(cuò)誤
if(interface.constructor !== Interface){
throw new Error("Function Interface.ensureImplements expects arguments two and above to be insterface of Interface")
}
// 遍歷存放接口名稱的數(shù)組
for (var j=0;j<interface.methods.length;j++){
var method = interface.methods[j]
if(!object[method] || typeof method !== 'function'){
throw new Error(`Function Interface.ensureImplements: object does not implement the ${interface.name} interface.Method ${method} was not found`)
}
}
}
}