- 論壇徽章:
- 0
|
Refactoring to Patterns : 用Adapter模式取代部分實現(xiàn)的接口 Kerievsky(原作)
關(guān)鍵字 refactoring pattern
用Adapter模式取代部分實現(xiàn)的接口
撰文/Joshua Kerievsky 編譯/透明
你的類實現(xiàn)一個接口,但是只為接口中的一部分方法提供代碼。
將實現(xiàn)的方法移交給該接口的適配器,并讓一個工廠方法可以訪問該適配器。
動機(jī)
具體類中的空方法總是讓我很煩心。我經(jīng)?匆娨恍┛辗椒,因為具體類經(jīng)常需要通過實現(xiàn)某個接口來滿足某種要求,但又不是真的需要實現(xiàn)接口中所有的方法。不需要的方法得到了聲明,但是保留為空:添加它們僅僅是為了滿足編譯器的規(guī)則。這可能不會讓你煩心,但的確讓我很煩心。我發(fā)現(xiàn),類的接口(即public方法集合)中如果出現(xiàn)空方法,不但會使類的接口變得臃腫,還會對類的行為做出錯誤的宣傳(我是一個類,我可以做X()、Y()和Z()——但我其實只提供X()的代碼),而且強(qiáng)迫我做一些不想做的工作(比如聲明一些空方法)。
Adapter模式提供了一種重構(gòu)這種代碼的好方法。Adapter類將給接口中所有的方法一個空的實現(xiàn),而你則可以從Adapter類派生出需要的子類,同時只需要提供需要的代碼。在Java中,我甚至不需要正式聲明一個Adapter子類:我只需要在Adapter類中創(chuàng)建一個匿名子類,然后直接將它的引用提供給Factory Method就行了。
通信
重復(fù)
簡化
不管是有人忘了刪掉空方法還是因為接口強(qiáng)迫你保留空方法,類中的空方法都不會有太多的通信。最好是只在真正實現(xiàn)的時候才進(jìn)行通信,而Adapter可以幫助你實現(xiàn)這一點。
如果你有不止一個類部分實現(xiàn)了同一個接口,那么你可能擁有相當(dāng)多的空方法了。只要讓這些類都與一個處理空方法聲明的Adapter類協(xié)同工作,你就可以去掉這些重復(fù)。
提供少量代碼總比提供大量代碼要容易。這個重構(gòu)方法給你一種方法以減少類中聲明的方法數(shù)量。另外,當(dāng)你用這種方法來適配多個接口時,它可以提供一條非常好的途徑,讓你可以實現(xiàn)每個接口中的一部分方法。
技巧
1. 如果還沒有要使用的接口(我把它叫做A)的適配器,就用“適配接口”的方法來創(chuàng)建一個。然后創(chuàng)建一個Factory Method模式,讓它返回你的適配器的一個實例(我把它叫做AdapterInstance)。
2. 把你的類中那些完全因為實現(xiàn)A接口才存在的空方法刪掉。
3. 把你實現(xiàn)了的那些由A指定的方法轉(zhuǎn)移到AdapterInstance中。
4. 刪除你的類中“implements A”的聲明。
5. 把AdapterInstance提供給需要的客戶。
示例
我將用具體的代碼來說明上面的步驟。在這里我們有一個名叫CardComponent的類,它派生自JDK中的Component類,并實現(xiàn)JDK中的MouseMotionListener接口。但是,它只實現(xiàn)MouseMotionListener接口的兩個方法中的一個。讓我們看看Adapter模式怎樣改善這段代碼。
第一步,為我們的AdapterInstance創(chuàng)建一個Factory Method。如果我們還沒有AdapterInstance,我們就需要用“適配接口”的重構(gòu)方法來創(chuàng)建一個。在這里,JDK已經(jīng)為MouseMotionListener接口提供了一個適配器,名叫MouseMotionAdapter。所以我們在CardComponet類中創(chuàng)建下列新方法,其中使用了Java便利的內(nèi)部類能力:
private MouseMotionAdapter createMouseMotionAdapter() {
return new MouseMotionAdapter() {
};
}
接著,我們刪掉CardComponent中聲明的空方法,因為它在MouseMotionListener已經(jīng)被實現(xiàn)了。在這里,MouseMotionListener實現(xiàn)了mouseDragged方法,但沒有實現(xiàn)mouseMoved方法。
public void mouseMoved(MouseEvent e) {}
現(xiàn)在我們已經(jīng)將mouseDragged方法從CardComponent方法中移到了MouseMotionAdapter的實例中。
private MouseMotionAdapter createMouseMotionAdapter() {
return new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
e.consume();
dragPos.x = e.getX();
dragPos.y = e.getY();
setLocation(getLocation().x+e.getX()-currPos.x,
getLocation().y+e.getY()-currPos.y);
repaint();
}
};
}
現(xiàn)在我們可以從CardComponent中刪掉MouseMotionListener的實現(xiàn)了。
public class CardComponent extends Container implements MouseMotionListener {
最后,我們必須將新的適配器實例提供給需要的客戶。在這里,我們必須觀察構(gòu)造子,它的代碼是這樣:
public CardComponent() {
//……
addMouseMotionListener(this);
}
需要將它改為調(diào)用新的私有工廠方法:
public CardComponent() {
//…
addMouseMotionListener(createMouseMotionAdapter());
}
現(xiàn)在我們進(jìn)行測試。很不幸,因為這段代碼與鼠標(biāo)相關(guān),我無法進(jìn)行自動化單元測試。因此我進(jìn)行了一些簡單的手工測試,并確認(rèn):我們的重構(gòu)很成功。
附錄:源代碼
l 重構(gòu)前:
public class CardComponent extends Container implements MouseMotionListener ...
public CardComponent(Card card,Explanations explanations) {
//...
addMouseMotionListener(this);
}
public void mouseDragged(MouseEvent e) {
e.consume();
dragPos.x = e.getX();
dragPos.y = e.getY();
setLocation(getLocation().x+e.getX()-currPos.x, getLocation().y+e.getY()-currPos.y);
repaint();
}
public void mouseMoved(MouseEvent e) {
}
l 重構(gòu)后:
public class CardComponent extends Container ...
public CardComponent(Card card,Explanations explanations) {
//...
addMouseMotionListener(createMouseMotionAdapter());
}
private MouseMotionAdapter createMouseMotionAdapter() {
return new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
e.consume();
dragPos.x = e.getX();
dragPos.y = e.getY();
setLocation(getLocation().x+e.getX()-currPos.x, getLocation().y+e.getY()-currPos.y);
repaint();
}
};
}
|
|