java 开发详解_面向接口编程详解-Java篇
相信看到這篇文字的人已經(jīng)不需要了解什么是接口了,我就不再過(guò)多的做介紹了,直接步入正題,接口測(cè)試如何編寫(xiě)。那么在這一篇里,我們用一個(gè)例子,讓各位對(duì)這個(gè)重要的編程思想有個(gè)直觀的印象。為充分考慮到初學(xué)者,所以這個(gè)例子非常簡(jiǎn)單,望各位高手見(jiàn)諒。
為了擺脫新手的概念,我這里也盡量不用main方法,而采用testNG編寫(xiě)測(cè)試用例。
定義:現(xiàn)在我們要開(kāi)發(fā)一個(gè)應(yīng)用,模擬移動(dòng)存儲(chǔ)設(shè)備的讀寫(xiě),即計(jì)算機(jī)與U盤(pán)、MP3、移動(dòng)硬盤(pán)等設(shè)備進(jìn)行數(shù)據(jù)交換。
上下文(環(huán)境):已知要實(shí)現(xiàn)U盤(pán)、MP3播放器、移動(dòng)硬盤(pán)三種移動(dòng)存儲(chǔ)設(shè)備,要求計(jì)算機(jī)能同這三種設(shè)備進(jìn)行數(shù)據(jù)交換,并且以后可能會(huì)有新的第三方的移動(dòng)存儲(chǔ)設(shè)備,所以計(jì)算機(jī)必須有擴(kuò)展性,能與目前未知而以后可能會(huì)出現(xiàn)的存儲(chǔ)設(shè)備進(jìn)行數(shù)據(jù)交換。各個(gè)存儲(chǔ)設(shè)備間讀、寫(xiě)的實(shí)現(xiàn)方法不同,U盤(pán)和移動(dòng)硬盤(pán)只有這兩個(gè)方法,MP3Player還有一個(gè)PlayMusic方法。
名詞定義:數(shù)據(jù)交換={讀,寫(xiě)}
解決方案列舉
方案一:分別定義FlashDisk、MP3Player、MobileHardDisk三個(gè)類(lèi),實(shí)現(xiàn)各自的Read和Write方法。然后在Computer類(lèi)中實(shí)例化上述三個(gè)類(lèi),為每個(gè)類(lèi)分別寫(xiě)讀、寫(xiě)方法。例如,為FlashDisk寫(xiě)ReadFromFlashDisk、WriteToFlashDisk兩個(gè)方法。總共六個(gè)方法。
方案二:定義抽象類(lèi)MobileStorage,在里面寫(xiě)虛方法Read和Write,三個(gè)存儲(chǔ)設(shè)備繼承此抽象類(lèi),并重寫(xiě)Read和Write方法。Computer類(lèi)中包含一個(gè)類(lèi)型為MobileStorage的成員變量,并為其編寫(xiě)get/set器,這樣Computer中只需要兩個(gè)方法:ReadData和WriteData,并通過(guò)多態(tài)性實(shí)現(xiàn)不同移動(dòng)設(shè)備的讀寫(xiě)。
方案三:與方案二基本相同,只是不定義抽象類(lèi),而是定義接口IMobileStorage,移動(dòng)存儲(chǔ)器類(lèi)實(shí)現(xiàn)此接口。Computer中通過(guò)依賴(lài)接口IMobileStorage實(shí)現(xiàn)多態(tài)性。
方案四:定義接口IReadable和IWritable,兩個(gè)接口分別只包含Read和Write,然后定義接口IMobileStorage接口繼承自IReadable和IWritable,剩下的實(shí)現(xiàn)與方案三相同。
下面,我們來(lái)分析一下以上四種方案:
首先,方案一最直白,實(shí)現(xiàn)起來(lái)最簡(jiǎn)單,但是它有一個(gè)致命的弱點(diǎn):可擴(kuò)展性差。或者說(shuō),不符合“開(kāi)放-關(guān)閉原則”(注:意為對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉)。當(dāng)將來(lái)有了第三方擴(kuò)展移動(dòng)存儲(chǔ)設(shè)備時(shí),必須對(duì)Computer進(jìn)行修改。這就如在一個(gè)真實(shí)的計(jì)算機(jī)上,為每一種移動(dòng)存儲(chǔ)設(shè)備實(shí)現(xiàn)一個(gè)不同的插口、并分別有各自的驅(qū)動(dòng)程序。當(dāng)有了一種新的移動(dòng)存儲(chǔ)設(shè)備后,我們就要將計(jì)算機(jī)大卸八塊,然后增加一個(gè)新的插口,在編寫(xiě)一套針對(duì)此新設(shè)備的驅(qū)動(dòng)程序。這種設(shè)計(jì)顯然不可取。
此方案的另一個(gè)缺點(diǎn)在于,冗余代碼多。如果有100種移動(dòng)存儲(chǔ),那我們的Computer中豈不是要至少寫(xiě)200個(gè)方法,這是不能接受的!
再看 方案二和方案三,之所以將這兩個(gè)方案放在一起討論,是因?yàn)樗麄兓臼且粋€(gè)方案(從思想層面上來(lái)說(shuō)),只不過(guò)實(shí)現(xiàn)手段不同,一個(gè)是使用了抽象類(lèi),一個(gè)是使用了接口,而且最終達(dá)到的目的應(yīng)該是一樣的。
我們先來(lái)評(píng)價(jià)這種方案:首先它解決了代碼冗余的問(wèn)題,因?yàn)榭梢詣?dòng)態(tài)替換移動(dòng)設(shè)備,并且都實(shí)現(xiàn)了共同的接口,所以不管有多少種移動(dòng)設(shè)備,只要一個(gè)Read方法和一個(gè)Write方法,多態(tài)性就幫我們解決問(wèn)題了。而對(duì)第一個(gè)問(wèn)題,由于可以運(yùn)行時(shí)動(dòng)態(tài)替換,而不必將移動(dòng)存儲(chǔ)類(lèi)硬編碼在Computer中,所以有了新的第三方設(shè)備,完全可以替換進(jìn)去運(yùn)行。這就是所謂的“依賴(lài)接口,而不是依賴(lài)與具體類(lèi)”,不信你看看,Computer類(lèi)只有一個(gè)MobileStorage類(lèi)型或IMobileStorage類(lèi)型的成員變量,至于這個(gè)變量具體是什么類(lèi)型,它并不知道,這取決于我們?cè)谶\(yùn)行時(shí)給這個(gè)變量的賦值。如此一來(lái),Computer和移動(dòng)存儲(chǔ)器類(lèi)的耦合度大大下降。
那么 這里該選抽象類(lèi)還是接口呢?還記得第一篇文章我對(duì)抽象類(lèi)和接口選擇的建議嗎?看動(dòng)機(jī)。這里,我們的動(dòng)機(jī)顯然是實(shí)現(xiàn)多態(tài)性而不是為了代碼復(fù)用,所以當(dāng)然要用接口。
最后 我們?cè)賮?lái)看一看方案四,它和方案三很類(lèi)似,只是將“可讀”和“可寫(xiě)”兩個(gè)規(guī)則分別抽象成了接口,然后讓IMobileStorage再繼承它們。這樣做,顯然進(jìn)一步提高了靈活性,但是,這有沒(méi)有設(shè)計(jì)過(guò)度的嫌疑呢?我的觀點(diǎn)是:這要看具體情況。如果我們的應(yīng)用中可能會(huì)出現(xiàn)一些類(lèi),這些類(lèi)只實(shí)現(xiàn)讀方法或只實(shí)現(xiàn)寫(xiě)方法,如只讀光盤(pán),那么這樣做也是可以的。如果我們知道以后出現(xiàn)的東西都是能讀又能寫(xiě)的,那這兩個(gè)接口就沒(méi)有必要了。其實(shí)如果將只讀設(shè)備的Write方法留空或拋出異常,也可以不要這兩個(gè)接口。總之一句話:理論是死的,人是活的,一切從現(xiàn)實(shí)需要來(lái),防止設(shè)計(jì)不足,也要防止設(shè)計(jì)過(guò)度。
在這里,我們姑且認(rèn)為以后的移動(dòng)存儲(chǔ)都是能讀又能寫(xiě)的,所以我們選方案三。
實(shí)現(xiàn)
下面,我們要將解決方案加以實(shí)現(xiàn)。我選擇的語(yǔ)言是Java,所以使用其他語(yǔ)言的朋友一樣可以參考。
首先編寫(xiě)IMobileStorage接口:
Code:IMobileStorage
1 public interfaceIMobileStorage {2
3 void Read(); //讀取數(shù)據(jù)
4 void Write(); //寫(xiě)入數(shù)據(jù)
5
6 }
代碼比較簡(jiǎn)單,只有兩個(gè)方法,沒(méi)什么好說(shuō)的,接下來(lái)是三個(gè)移動(dòng)存儲(chǔ)設(shè)備的具體實(shí)現(xiàn)代碼:
U盤(pán)
Code:FlashDisk
1 public class FlashDisk implementsIMobileStorage{2 @Override3 public voidRead() {4 System.out.println("Reading from FlashDisk……");5 System.out.println("Read finished!");6 }7
8 @Override9 public voidWrite() {10 System.out.println("Writing to FlashDisk……");11 System.out.println("Write finished!");12 }13 }
MP3
Code:MP3Player
public class MP3Player implementsIMobileStorage{
@Overridepublic voidRead() {
System.out.println("Reading from MP3Player……");
System.out.println("Read finished!");
}
@Overridepublic voidWrite() {
System.out.println("Writing to MP3Player……");
System.out.println("Write finished!");
}public voidPlayMusic(){
System.out.println("Music is playing……");
}
}
移動(dòng)硬盤(pán)
Code:MobileHardDisk
public class MobileHardDisk implementsIMobileStorage{
@Overridepublic voidRead() {
System.out.println("Reading from MobileHardDisk……");
System.out.println("Read finished!");
}
@Overridepublic voidWrite() {
System.out.println("Writing to MobileHardDisk……");
System.out.println("Write finished!");
}
}
可以看到,它們都實(shí)現(xiàn)了IMobileStorage接口,并重寫(xiě)了各自不同的Read和Write方法。下面,我們來(lái)寫(xiě)Computer:
Code:Computer
public classComputer {privateIMobileStorage _usbDrive;publicIMobileStorage get_usbDrive() {return_usbDrive;
}public voidset_usbDrive(IMobileStorage _usbDrive) {this._usbDrive =_usbDrive;
}publicComputer(){}publicComputer(IMobileStorage _usbDrive) {this._usbDrive =_usbDrive;
}public voidReadData(){this._usbDrive.Read();
}public voidWriteData(){this._usbDrive.Write();
}
}
其中的UsbDrive就是可替換的移動(dòng)存儲(chǔ)設(shè)備,之所以用這個(gè)名字,是為了讓大家覺(jué)得直觀,就像我們平常使用電腦上的USB插口插拔設(shè)備一樣。
OK!下面我們來(lái)測(cè)試我們的“電腦”和“移動(dòng)存儲(chǔ)設(shè)備”是否工作正常。我是用的Java控制臺(tái)程序打印結(jié)果,具體代碼如下:
Code:測(cè)試代碼
public classToTest {
@Testpublic voidprogram1(){
Computer computer= newComputer();
IMobileStorage mp3Player= newMP3Player();
IMobileStorage flashDisk= newFlashDisk();
IMobileStorage moblieHardDisk= newMobileHardDisk();
System.out.println("I inserted my MP3 Player into my computer and copy some music to it:");
computer.set_usbDrive(mp3Player);
computer.WriteData();
System.out.println("====================");
System.out.println("Well,I also want to copy a great movie to my computer from a mobile hard disk:");
computer.set_usbDrive(moblieHardDisk);
computer.ReadData();
System.out.println("====================");
System.out.println("OK!I have to read some files from my flash disk and copy another file to it:");
computer.set_usbDrive(flashDisk);
computer.ReadData();
computer.WriteData();
System.out.println();
}
運(yùn)行結(jié)果如下:
圖2.1 各種移動(dòng)存儲(chǔ)設(shè)備測(cè)試結(jié)果
好的,看來(lái)我們的系統(tǒng)工作良好。
后來(lái)……
剛過(guò)了一個(gè)星期,就有人送來(lái)了新的移動(dòng)存儲(chǔ)設(shè)備N(xiāo)ewMobileStorage,讓我測(cè)試能不能用,我微微一笑,心想這不是小菜一碟,讓我們看看面向接口編程的威力吧!將測(cè)試程序修改成如下:
(NewMobileStorage的類(lèi)請(qǐng)參照u盤(pán)、移動(dòng)硬盤(pán)等類(lèi)編寫(xiě)……也可以自創(chuàng))
測(cè)試代碼
@Testpublic voidprogram2(){
Computer computer= newComputer();
IMobileStorage newMobileStorage= newNewMoblieStorage();
computer.set_usbDrive(newMobileStorage);
newMobileStorage.Write();
newMobileStorage.Read();
}
運(yùn)行結(jié)果:
圖2.2 新設(shè)備擴(kuò)展測(cè)試結(jié)果
又過(guò)了幾天,有人通知我說(shuō)又有一個(gè)叫SuperStorage的移動(dòng)設(shè)備要接到我們的Computer上,我心想來(lái)吧,管你是“超級(jí)存儲(chǔ)”還是“特級(jí)存儲(chǔ)”,我的“面向接口編程大法”把你們統(tǒng)統(tǒng)搞定。
但是,當(dāng)設(shè)備真的送來(lái),我傻眼了,開(kāi)發(fā)這個(gè)新設(shè)備的團(tuán)隊(duì)沒(méi)有拿到我們的IMobileStorage接口,自然也沒(méi)有遵照這個(gè)約定。這個(gè)設(shè)備的讀、寫(xiě)方法不叫Read和Write,而是叫rd和wt,這下完了……不符合接口啊,插不上。但是,不要著急,我們回到現(xiàn)實(shí)來(lái)找找解決的辦法。我們一起想想:如果你的Computer上只有USB接口,而有人拿來(lái)一個(gè)PS/2的鼠標(biāo)要插上用,你該怎么辦?想起來(lái)了吧,是不是有一種叫“PS/2-USB”轉(zhuǎn)換器的東西?也叫適配器,可以進(jìn)行不同接口的轉(zhuǎn)換。對(duì)了!程序中也有轉(zhuǎn)換器。
這里,我要引入一個(gè)設(shè)計(jì)模式,叫“Adapter”。它的作用就如現(xiàn)實(shí)中的適配器一樣,把接口不一致的兩個(gè)插件接合起來(lái)。由于本篇不是講設(shè)計(jì)模式的,而且Adapter設(shè)計(jì)模式很好理解,所以我就不細(xì)講了,先來(lái)看我設(shè)計(jì)的類(lèi)圖吧:
如圖所示,雖然SuperStorage沒(méi)有實(shí)現(xiàn)IMobileStorage,但我們定義了一個(gè)實(shí)現(xiàn)IMobileStorage的SuperStorageAdapter,它聚合了一個(gè)SuperStorage,并將rd和wt適配為Read和Write,SuperStorageAdapter(這里注意自行編寫(xiě)SuperStorage的類(lèi)和他用到的接口)
圖2.3 Adapter模式應(yīng)用示意
具體代碼如下:
Code:SuperStorageAdapter
1 public class SuperStorageAdapter implementsIMobileStorage {2 privateSuperStorage _superStorage;3
4 publicSuperStorage get_superStorage() {5 return_superStorage;6 }7
8 public voidset_superStorage(SuperStorage _superStorage) {9 this._superStorage =_superStorage;10 }11
12 @Override13 public voidRead(){14 this._superStorage.rd();15 }16
17 @Override18 public voidWrite() {19 this._superStorage.wt();20 }21 }
好,現(xiàn)在我們來(lái)測(cè)試適配過(guò)的新設(shè)備,測(cè)試代碼如下:
Code:測(cè)試代碼
@Testpublic voidprogram3(){
Computer computer= newComputer();
SuperStorageAdapter superStorageAdapter= newSuperStorageAdapter();
SuperStorage superStorage= newSuperStorage();
superStorageAdapter.set_superStorage(superStorage);
System.out.println("Now,I am testing the new super storage with adapter:");
computer.set_usbDrive(superStorageAdapter);
computer.ReadData();
computer.WriteData();
System.out.println();
}
運(yùn)行結(jié)果:
圖2.4 利用Adapter模式運(yùn)行新設(shè)備測(cè)試結(jié)果
OK!雖然遇到了一些困難,不過(guò)在設(shè)計(jì)模式的幫助下,我們還是在沒(méi)有修改Computer任何代碼的情況下實(shí)現(xiàn)了新設(shè)備的運(yùn)行。希望各位朋友結(jié)合第一篇的理論和這個(gè)例子,仔細(xì)思考面向接口的問(wèn)題。當(dāng)然,不要忘了結(jié)合現(xiàn)實(shí)。
原文是C#的語(yǔ)言,在這里做了java的轉(zhuǎn)換編寫(xiě),感謝原作者,轉(zhuǎn)自C站的一小平民:http://blog.csdn.net/boer521314/article/details/40378151
總結(jié)
以上是生活随笔為你收集整理的java 开发详解_面向接口编程详解-Java篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java jvm 加载_Jvm是如何加载
- 下一篇: 原子变量 java_Java原子变量详解