物件導向設計的第一條原則
介面是物件導向javascript程式員的工具箱中最有用的工具之一。Gof在書中提到,物件導向設計的第一條原則就是:“針對介面編程而不是實現編程”。
javascript 沒有介面
然而javascript本身沒有內建的建立或實現介面的方法。也就說javascript本身是沒有介面的。不過、由於javascript有強大的靈活性,最終我們可以在javascript中類比實現介面。
介面定義,什麼是介面
介面提供了一種用來說明一個對象應該具有的哪些方法的手段,它可以表明這些方法的語義,但是不規定這些方法的具體實現。呵呵感覺不怎麼專業,很拗口呀。
介面利弊
先講好處:
1、介面能促進代碼重用
2、可以穩定不用類之間的通訊方式
3、方便測試和調試
壞處:
1、介面強化了類型的作用,降低了javascript語言的靈活性。
2、javascript 沒有原生的介面支援
3、javascript模仿介面帶來效能的影響
4、javascript不能強制其他程式員遵守你的介面定義(這個要靠行政手段,呵呵)
其他語言的介面實現
其他語言基本使用了interface和implements關鍵字,內建支援。
javascript中模仿介面的方法
1、用注釋描述介面
用注釋模仿介面是最簡單的方法,就是在注釋裡面寫上介面的定義資訊。這個超級簡單,但效果就不怎麼地了。
例子如下:
Java代碼
/**
//這裡是介面的描述
interface XXXInterFace{
function add(child);
function remove(child);
}
*/
var XXXClass=function(){//implements XXXInterFace 說明實現了介面
}
XXXClass.prototype.add=function(){
......
}
XXXClass.prototype.remove=function(){
......
}
2、用屬性檢查模仿介面
這個方法比第一個要嚴謹一些。所有的類必須明確的聲明自己實現的介面。雖然介面依舊是注釋,但是你可以檢查某個屬性得知實現了什麼樣的介面。
Java代碼
/**
//這裡是依舊是介面的描述
interface XXXInterFace{
function add(child);
function remove(child);
}
*/
var XXXClass-function(){
this.implementsInterfaces=["XXXInterFace"];//申明實現的介面
}
XXXClass.prototype.add=function(){
......
}
XXXClass.prototype.remove=function(){
......
}
//調用類的方法
function dosome(xxxClassInstance){
if(!implements(xxxClassInstance,"XXXInterFace")){//檢查實現的介面
alert("沒有實現 XXXInterFace")
}
}
function implements(obj){//檢查實現的介面的方法
......
}
3、鴨式辨型模仿介面
鴨式辨型(鴨子測試DUCK TEST)——像鴨子一樣走路並且嘎嘎叫的就是鴨子。呵呵這個很有意思,書上說的,這個是james Whitcomb Riley的名言。大家都知道這個大神不?我google了一下,並在前面的串連上加上了基維百科的串連。
寫道
美國印地安納詩人詹姆斯·惠特科姆·萊利(1849-1916)可能發明了這個說法。他寫道:“當我看到一隻鳥,它走路像鴨子,遊泳像鴨子,叫聲像鴨子,我就稱其為鴨子。”
言歸正傳,這個方法比較前面來說,不用藉助於注釋,而且可以強制實施,但是多了輔助的工具類InterFace和一個輔助函數ensureImplements。
Java代碼
//定義申明介面
var XXXInterFace=new InterFace("XXXInterFace",["add","remove"]);
//調用方法
function doSome(instance){
ensureImplements(instance,XXXInterFace);//介面檢查
}
4、本書的介面實現,Interface類
書中使用了第一中和第三種的結合,也就是我既寫好了注釋告訴你我實現的介面,也用到了鴨子辨型的介面檢查。呵呵也就是要我們要是用介面的話,也可以模仿這樣來實現。還有就是提醒大家注意好好寫寫注釋。
書中具體的InterFace類如下:
Java代碼
// Constructor.呵呵抄不下去了網上下了原始碼
var Interface = function(name, methods) {
if(arguments.length != 2) {
throw new Error("Interface constructor called with " + arguments.length
+ "arguments, but expected exactly 2.");
}
this.name = name;
this.methods = [];
for(var i = 0, len = methods.length; i < len; i++) {
if(typeof methods[i] !== string) {
throw new Error("Interface constructor expects method names to be "
+ "passed in as a string.");
}
this.methods.push(methods[i]);
}
};
// Static class method.
Interface.ensureImplements = function(object) {
if(arguments.length < 2) {
throw new Error("Function Interface.ensureImplements called with " +
arguments.length + "arguments, but expected at least 2.");
}
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 arguments "
+ "two and above to be instances of Interface.");
}
for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
var method = interface.methods[j];
if(!object[method] || typeof object[method] !== function) {
throw new Error("Function Interface.ensureImplements: object "
+ "does not implement the " + interface.name
+ " interface. Method " + method + " was not found.");
}
}
}
};
依賴介面的設計模式
原廠模式
組合模式
裝飾者模式
命令模式