在Java中,有這樣一段老代碼:
Java代碼
class Round{
public void drawRound(); //畫圓
}
現在新代碼希望能和它共存,使用一個Person的對象來控制,只不過,可能drawRound,也可能drawRect啊:
Java代碼
class Rect{
public void drawRect(); //畫方
}
好,廢話少說,我先想到了Adapter模式:
Java代碼
interface Drawable{
public void draw();
}
public class RoundAdapter implements Drawable{
private Round round;
public void draw(){
round.drawRound();
}
}
public class RectAdapter implements Drawable{
private Rect rect;
public void draw(){
rect.drawRect();
}
}
然後,我再引入一個Person對象,就能搞定這一切了:
Java代碼
class Person{
private Drawable adapter;
public Person(Drawable adapter){
this.adapter = adapter;
}
public void draw(){
this.adapter.draw();
}
}
Drawable rou = new RoundAdapter();
Drawable rec = new RectAdapter();
new Person(rou).draw(); //畫圓
new Person(rec).draw(); //畫方
想必到此已經讓你煩了,一個Adapter模式的最簡單例子。再多看一看,這個模式的核心是什嗎?介面!對,正是例子中的Drawable介面——正是在介面的規約和領導下,我們才能讓畫圓和畫方都變得那麼聽話。
現在JavaScript中,也有這樣一段老代碼:
Java代碼
function Round(){
this.drawRound = function(){
alert("round");
}
}
我也想依葫蘆畫瓢,但是JavaScript沒有介面了,怎麼辦?
……
介面的作用是什嗎?是對類的行為的規約,可是JavaScript的行為是動態,無法用簡單純粹的介面來實現、來約束,即便類比出這樣一個介面(參見《JavaScript Design Pattern》),在此又有必要使用它嗎?強行做出一個介面來,這不是和JavaScript的初衷相違背了嗎?
再回到這個問題上面,我原本希望Person的對象可以調用一個統一的draw方法,只是在通過構造Person對象的時候,傳入一個不同實現的Drawable對象,做出了不同約束下的實現。
那麼,JavaScript中,不僅僅方法的調用者可以作為一個參數傳入,方法本身也可以作為參數傳入(即所謂方法閉包),這樣,所有變化點都控制在這個參數之中,不也實現了我想要的介面規約的效果嗎:
Java代碼
function Rect(){
this.drawRect = function(){
alert("rect");
}
}
function Person(obj){
//obj參數的格式:{doWhat,who}
for(var i in obj){
this.doWhat = i;
this.who = obj[i];
break;
}
this.draw = function(){
this.who[this.doWhat].call(this.who);
};
}
var rou = { drawRound : new Round() };
var rec = { drawRect : new Rect() };
(new Person(rou)).draw();
(new Person(rec)).draw();
寫到這裡,我覺得很開心:
在Java中,通過介面的規約和適配器的協助,我將變化點封裝在Person構造器的參數之中;
JavaScript中,沒有了介面、脫離了適配器的協助,我依然能將變化點封裝在Person的構造器參數之中。
作者“四火的BLOG”