JAVA設計模式什麼鬼(介面卡)——作者:凸凹裡歐
我們這個世界,充滿著千奇百怪的物件,更有趣的是物件與物件間是存在著互動,溝通,這樣世界才變得美妙。那到底是怎樣互動呢?靠什麼才能互動呢?是的,介面。比如你和朋友一起喝茶聊天,我們暫且不管聲帶,耳膜這些功能性物件, 那你們之間聊天的介面就是嘴巴耳朵了,嘴巴傳送聲波,耳朵接收聲波,介面,一定是輸入或輸出的終端。
好了,這是語言溝通,那如果是行為溝通呢,比如在一個夜黑風高的晚上,你跟你的另一半偷偷鑽進了高粱地裡並做了一些喪盡天良的事情,到底是通過什麼介面互動呢?好吧,這個難題留個你了,好好研究一下留言給我。
好了我們言歸正傳,如果說你跟模里西斯人交流,你們之間的介面對接失效了,說什麼完全根本聽不懂!怎樣跨越語言的鴻溝?找個翻譯吧,那我們說這個翻譯就扮演了一個介面卡(Adapter)的角色,其實翻譯官們為中日友好做出了巨大貢獻,別說是吃你幾個爛西瓜。
顧名思義,介面卡,得適應當前的不同配置,解決相容性問題。我們生活中充滿了各種各樣的介面卡,上網用的調變解調器(modem)就是一種數模轉換的介面卡,俗稱“貓”,不過現在都是光貓了,也就是光訊號和電訊號的互相轉化,其實道理是一樣的,還有各種變壓器也屬於電壓轉換的介面卡。
如果覺得還不夠形象可以看一下家裡的電器,比如你的電視是兩項插頭,牆上的插孔是三項插孔怎麼辦?哦,有人說把插頭掰彎強行插入!那如果是三項插頭接兩項插孔呢?把零線插針拔了!呃,我只能說這是暴力破解!違反設計模式原則。言歸正傳,我們還是不要隨便破壞現有的類,那我們需要的是一個轉換器,用優雅微妙的方式化解這種不相容情況。
舉個例子,我們開始程式碼部分,先寫牆上的三項插孔介面,命名TriplePin:
public interface TriplePin {
//引數分別為火線live,零線null,地線earth
public void electrify(int l, int n, int e);
}
我們只定義三插孔標準electrify(通電)方法,三個引數分別是火線、零線、地線,很簡單吧,同樣地接下來是兩項插孔介面,只是少了地線,命名DualPin:
public interface DualPin { public void electrify(int l, int n);//這裡沒有地線 }
請注意,這個並不是我們的牆上的目標介面,而是電視機的兩插標準。好了繼續,我們的TV登場了,用的是兩項插頭,當然它實現的是DualPin的標準,Let's keep it simple,命名TV:
public class TV implements DualPin {
@Override//既然是兩項插頭,當然實現兩項插標準
public void electrify(int l, int n) {
System.out.println("火線通電:" + l);
System.out.println("零線通電:" + n);
}
}
那麼問題來了,牆上的介面是三插標準,電視實現的是兩插標準,無法通電。怎麼辦?把電視拆了重新修改實現三插標準麼?暴力份子你又來?答案顯然是否定的,既然是設計模式,果斷轉換插頭啊!好,寫個Adapter解決他們之間不可調和的矛盾。
1 public class Adapter implements TriplePin {
2
3 private DualPin dualPinDevice;
4
5 //建立介面卡地時候,需要把雙插裝置接入進來
6 public Adapter(DualPin dualPinDevice) {
7 this.dualPinDevice = dualPinDevice;
8 }
9
10 //介面卡實現的是目標介面
11 @Override
12 public void electrify(int l, int n, int e) {
13 //實際上呼叫了被適配裝置的雙插通電,地線e被丟棄了。
14 dualPinDevice.electrify(l, n);
15 }
16
17 }
注意了最關鍵最精華的部分來了,第3行程式碼意味著這個介面卡內部是有一個雙插介面的,對於任何雙插標準的裝置都是可以相容的OK嗎?不明白趕緊看看你家裡的介面卡。第6行的程式碼完成的過程實際就是你把電視插頭接入Adapter了,其實介面卡並不在意是什麼裝置,洗衣機冰箱都可以的,只要是雙插標準就可以接入(第一節講過的多型概念)。第12行通電方法實現的是三插標準,但方法體內部第14行實際上是在給“某個裝置”(是什麼裝置就看你接什麼了)的雙插供電,地線e那個引數是用不上的,所以就沒有接通,很清晰透徹吧?
當然,除了以上的注入插頭的方式(物件適配),還有另一種更簡單的方式叫做“類介面卡”我們來看下:
1 public class ClassAdapter extends TV implements TriplePin{
2
3 @Override
4 public void electrify(int l, int n, int e) {
5 super.electrify(l, n);
6 }
7
8 }
看出來區別沒有?這裡並沒有注入插頭(物件組合),而是把電視機給繼承了,這樣就可以直接呼叫父類(TV)的雙插通電而不是注入進來去呼叫,缺點大家也看到了,這介面卡繼承為TV兒子專用了,洗衣機是用不了啦,作死?其實也不是完全不好,要看具體應用場景哈。
至此,我們的Adapter就差不多完成了,以後再也不用破壞插頭了,因為這樣重寫介面或者修改類的代價太大,如果其他類還有依賴的話,那統統要修改,引入了沒有必要的重構,總之暴力修改是違反設計模式的基本原則的,開閉原則,指的就是對擴充套件開放,而對修改關閉,也就是說不要去改動原始類,而是擴充套件現有功能,提供另一種機制讓整個系統實現想要的功能。
最後說下那些概念,歸類,名字,什麼“類介面卡”,“物件介面卡”啊,其實,理解不了就算了無所謂,真正的意義在於怎麼樣在實際工作中靈活運用,實現方式是無窮無盡的,道不清說不盡的,沒必要太糾結它到底叫什麼,歸於哪一類,掌控其背後的道才是最根本的,正如李耳君所言:“道可道,非常道。名可名,非常名。”