原帖:http://topic.csdn.net/t/20030921/23/2285054.html
“介面(Interface)是什麼,為什麼使用介面,如何恰當的使用介面?”
----------------------------------------------------------------------------------------------------------------------------
(wulemale)
一個類,當需要實現多重繼承時就要用到介面了
如,定義一個多線程的小應用程式類,實現的方法之一如下:
public class ThreadTest extends Applet implements Runnable
{
ThreadTest myTest=new ThreadTest();
Thread myThread=new Thread(myTest);
myThread.start();
}
----------------------------------------------------------------------------------------------------------------------------
(聰明的一豬)
介面就是當你的模組需要外面提供一個功能的時候,你用來定義你期望的功能是什麼樣的。
它作為對你的需要的一個規範,一個描述。
你通過介面,精確描述你需要的“功能”,然後從外界接收一個該介面的對象。
然後,你就可以使用這個介面了。
它讓你關注“介面”,關注“規範”,而不是“實現”。別人怎麼實現,那是別人的事,與我無關。你可以對你自己需要實現的東西編碼,編譯,測試,即使你需要的功能別人還沒有實現出來。
根據需要,別人可以向你的模組傳遞不同的介面實現,從而達到重用的目的。
舉個例子:
你給人寫一個旅遊指南:“先乘車去北海,在此吃午餐,下午去故宮。”
至於乘bus還是taxi, 吃包子還是麥當勞,下午怎麼去故宮,不要越俎代庖,使用者可以根據自己的客觀情況決定。
於是,乘車,吃午餐,去故宮,全是介面。你只管寫你關心的邏輯,至於一些你不關心的實現細節,用介面抽象出來。
最後:介面和多重繼承沒有關係。
----------------------------------------------------------------------------------------------------------------------------
(鄭)
可以從複用的角度來看。
比如我現在有一個class : ClassA
有很多程式使用這個ClassA, 都寫在代碼裡面。如果後來有一個ClassB,功能比ClassA要好一些,我要所有用ClassA的地方都變成ClassB,想必會非常麻煩。
但是,如果我事先ClassA 是實現了一個介面IClass,所有的代碼都是針對IClass編成的,執行個體化ClassA的地方都用一個工廠類來執行個體化的,這樣,當我需要把所有ClassA都換成ClassB的時候只要將ClassB實現IClass,然後在工廠類中將返回ClassA的執行個體換成返回ClassB的執行個體就可以了。
----------------------------------------------------------------------------------------------------------------------------
(csdnhbc20)
介面相對來說是不變化的,也就是穩定的,所以,定義介面的時候,要把本質的東西找出來,然後放在介面裡面,這也就是所謂的介面的永恒,而實際上又不可能達到永恒的,因為具體的環境在變化,軟體的實質是變化的,所以永遠都沒有方法能定義出一個永恒的介面。
具體抽象出介面的方法,我覺得主要是幾個常見的oo原則:
1:ocp原則
2:裡氏代換原則(父類可以用的地方,子類肯定也可以)
3:依賴倒轉原則(依賴介面類,不依賴實作類別)
....
實際工作中,其實主要的還是要對所做的產品行業有細緻的分析(也就是需求分析這一階段)
,能夠充分瞭解該系統領域的東西,這樣才能預見出該系統中的變化,才能抽象出比較好的對象出來
這樣你做的系統自然就是客戶所滿意的,所以,實際上是要擴大我們的橫向思維能力,有深厚的人文,心裡,社會知識,這樣才能比較真實的把握系統的需求,同時也要求有很強的接收新問題的能力,自學能力。
----------------------------------------------------------------------------------------------------------------------------
(天才一樣的白癡)
介面可以看成是類似抽象類別的東西,他只關心某種共性,但並不給出實現。
我覺得比較重要的是要搞清楚什麼時候用抽象類別,什麼時候用介面。
一般來說抽象類別就是 is something 的關係,而介面要描述的是 has something 的關係。
比如你可以定義一個介面鎖,那麼鎖可能會有各種各樣的鎖,門鎖 車鎖 電腦鎖等等,他們的特性都是可以lock unlock諸如此類,但是具體鎖的是門還是車還是電腦等等就要在這些鎖的具體定義裡面寫了。甚至可能門鎖是一個抽象類別 車鎖是一個抽象類別 電腦鎖是一個抽象類別,它們都聲明了鎖這個介面,然後我就可以從這些抽象類別裡面繼承出防盜門鎖,電腦門鎖諸如此類的具體類了
----------------------------------------------------------------------------------------------------------------------------
(xuefeng)
介面使得類的定義和實現完全分開。
類的提供者向客戶提供一個介面,保證實現介面所定義的所有功能,至於如何?,那是他的事,與你無關。
作為用戶端,只需要關心介面提供的功能,使用介面就可以了。
這樣做最大限度的降低了層與層之間的耦合,提供者可能會更改實作類別,但只要介面不變,用戶端無需改動。
java編程最重要的一點就是:只針對抽象編程。
----------------------------------------------------------------------------------------------------------------------------
(蜀人)
interface 是關於的類型的,而class 是關於實現的,在class中有資料成員,如果修改了class,那麼client就必須要重新串連,甚至編譯。而通過interface的話,就可以不用重新編譯,串連。
----------------------------------------------------------------------------------------------------------------------------
(jeffyan77)
bruce007說的比較接近我的觀點:interface 是關於類型的,而class 是關於實現的。
不要因為實現中的某種方便而使用interface。
前面幾位講到的使用interface 的好處,基本上就是使用純類型,以及將純類型和實現區分開的好處。都是正確的。
----------------------------------------------------------------------------------------------------------------------------
(趙明宇)
1. 從需求上來說:介面體顯了你需求分析的結果。它是需求分析後的產物。你在做完需求分析後,完全可以得到一個系統各部所需要的方法。記住,它只反應了系統想要做些什麼,而不是系統該怎麼做。當你認為你所定義的介面完全滿足了需求中的全部功能時,最好通過手工的方式類比系統的運行。
舉個例子,你可以一邊看著你的使用案例圖和順序圖表(不僅僅是這些),一邊“調用”你的介面中的方法。這個調用是指在你的頭腦中調用。你就直接假設所有的實現都已經完成,並且非常正確。
如果在你的頭腦中系統“運行”的很好。說明你完成了系統上層的設計工作(前提是你的需求是正確的)。接下來你可以針對你所設計的介面做應對於“在介面所定義的方法下不同需求的不同的實現”來實現對相同的業務不同的業務規責的多態性。記住介面是實現多態性的機制(當然,抽象類別也可以)。
2. 從設計角度來分析:java提倡的是針對抽象編程(其實只要是物件導向的語言都應如此)。具體的意思就是用戶端應該只依賴於抽象的東西。再具體一點來說,就是說白了,用戶端應該只針對介面來編程。不要針對具體的實現,這樣當實現這個介面的商務規則發生了變化,你只需要改動後面的實現,而不影響用戶端,因為用戶端根本不知道你已經“偷偷地”改變了方法的具體實現。這使得系統一處發生了變化,對系統的影響範圍減至最少。順便說一下“用戶端”的概念,用戶端不是使用者端。用戶端是相對的,A 調用了 B ,A就叫做B的用戶端,B是A的服務端.一般來講,一個系統中,上層是下一層的用戶端。下一層是上一層的服務端。一個系統的各部分可能相互調用,那它們之間也是用戶端和服務端的關係。服務端只提供給用戶端必要的方法。當系統各部分之間針對於抽象(具體就是通過介面),來相互調用的話,各部分之間就有了一個穩定的“契約”,它們都以這個契約為準相互溝通,而彼此都不知道對方是如何?的。這樣的話,你就有很大的餘地來選擇每部分的具體實現細節,怎麼實現。從而一點都不影響它們的用戶端。這種情況在實現規則發生改變你需要重寫實現時體現的最為明顯。這也是針對抽象編程的原因。
3. 從物件導向的角度看:物件導向技術為我們提供了許多好的特徵。比如:資訊隱藏,多態。實現資訊隱藏的手段是封裝,而實現多態的手段就是繼承。(介面的實現可以看做是一種繼承,雖然它沒有從“父親”那裡繼承任何東西,但它還是你的父親 ^_^)。運用介面我們可以充份運用多態的特性。要知道,多態是物件導向中最為重要的特性之一。如果你不用介面或抽象類別,而只是直接用每個具體的類做事情,那可以說你大概從物件導向這裡僅僅取得了一點封裝資訊的好處,而失去了多態這個最大的好處。這樣的程式並不是真正運用了物件導向的技術。所以用物件導向,最主要的就是在用它的多態性質。而使用介面正是通往多態大道。
以上是我從我的經驗中總結的一點個人意見。大家繼續討論一下。
(趙明宇)
對了,順便說一下多繼承的事情。介面的存在並不是因為多繼承的原因,介面最重要的用途也不是多繼承。只是因為java不支援多繼承,而運用介面可以間接地實現這種性質,所以總讓人感覺介面是因為多繼承的原因而出現的。這是大錯特錯了。
我個人感覺多繼承這種特性大大的增加了設計的複雜度,而它帶來的好處卻不像它帶來的壞處那樣大。設計的時候盡量不要使用多繼承。java取消了多繼承的這種性質應該是有她的原因的。
(趙明宇)
繼承是應該用的,但不要過渡依賴,就像 ajoo(聰明的一豬) 說的一樣。
設計時應該首先考慮一下彙總和組合的情況,其次考慮繼承。 繼承是靜態複用方式,對象之間的彙總關係是動態複用。不要只因為一個類已經提供了大部分你需要的代碼,而你又想增加一些就去繼承那個類。這往往都是錯誤的想法。繼承一定要滿足 is-a 的關係。也就是說只有當子類是一種父類的時候你才可以去做繼承。 is-a 關係是裡式替換的必要條件。
(趙明宇)
//這是一個介面,Vehicle是交通工具的意思,這個介面定義了交通工具的一個共有的方法
//drive()駕駛
public interface InfVehicle {
public void drive();
}
//小汽車是交通工具,實現交通工具的介面
public class Car implements InfVehicle {
public void drive() {
//這裡具體實現小汽車的駕駛方法
}
}
//單車也是交通工具,實現了交通工具的介面
public class Bicycle implements InfVehicle {
public void drive() {
//這裡具體實現單車的駕駛方法
}
}
//這是一個“人”類,它有一個方法是 goHome回家,回家需要一種交通工具,所以他有一個交通工具,他回家時使用這個工具
public class Man {
private InfVehicle vehicle;
public InfVehicle getVehicle() {
return this.vehicle;
}
public void setVehicle(InfVehicle vehicle) {
this.vehicle = vehicle;
}
//回家方法
public void goHome() {
this.vehicle.drive();
}
}
現在我們來看怎麼使用這些類
Man aMan = new Man(); //建立一個 "人 "
InfVehicle car = new Car(); //建立一個小汽車
InfVehicle bicycle = new Bicycle();//建立一個單車
//比如今天這個人想開車回家,我們就
aMan.setVehicle(car);
aMan.goHome();
//如果他開車開膩了,想換一種方式,他這可以騎車回家
aMan.setVehicle(bicycle);
aMan.goHome();
-----------------------------------------------------------------
這樣的代碼的好處就是,如果有一天那個人說:“我不想開小汽車了,沒意思,我也不想騎車,太累。我想開大卡車下班回家,那樣夠帥!”(注意:客戶完全可以提出類似的要求)那我們怎麼辦呢? 在這裡,人這個類使用交通工具的服務,人即是用戶端,交通工具是服務端(上面我的一個貼子說了用戶端和服務端的事情),那我們不想對用戶端造成影響。因為在這個例子中使用了介面,我們就很好辦。 比如我們可以添加一個卡車類Sixby實現交通工具的介面,這樣如果這個人想開卡車下班回家,就傳給它一個Sixby的執行個體,他就可以夠帥了^_^
當然,這裡有一個問題,但它本貼討論的介面無關。就是我們怎麼來建立具體的交通工具? 是不是每次要換交通工具的時候都要重新new一個? 對這個問題你就可以考慮一下工廠的設計模式了。這話就說遠了,已經與本主題無關了。
(趙明宇)
(珂兒)
也許我有點笨,上面高手講了那麼多,我卻還是沒有理解介面和抽象類別的區別,抽象類別不也可以實現多態嗎?就像 truezerg(趙明宇) 寫的例子,我感覺如果聲明一個抽象類別,那麼在具體運用的時候,可以重載這些方法,一樣可以達到這種效果呀。
剛才問了一下同事,他說介面比較靈活,給你一個介面可以訪問遠端,有點迷茫,不太理解。
(趙明宇)
to: yangjuanli(珂兒)
向我上面寫的那個例子是非常簡單的,只是想表達一下怎麼來使用介面。當然在那個例子中如果你能找到所有的交通工具在駕駛上的一個共同點的話,使用抽象類別也是可以的。這種情況你就可以把共同的地方向上轉移到父類中。這種情況你就可以使用抽象類別了。
你要注意的一點是,介面是與抽象更密切的,抽象類別是與實現更密切的(和介面相比較,雖然它叫抽象類別)。所以一旦觸及到實現,你就要特別注意和小心。你必須確認你所放在抽象類別中的代碼一定是適合所有它的子類的,如果不是這樣的話或是你無法確定,你就使用介面比較好一點。
另外我個人的建議是不管你有沒有抽取出有共性的代碼,不管你有沒有用到抽象類別,你最好總是定義一個介面給使用者。即便是你有抽象類別,你也要提供一個介面,讓你的抽象類別實現你的介面,你的子類繼承你的抽象類別。把介面給使用者。 這讓比較好一點。
breakpoint_fish@hotmail.com
歡迎同行加入!
----------------------------------------------------------------------------------------------------------------------------
(秋天的樹)
舉個例子,如J2EE,sun發布了一個新的規範,全部使用interface,而IBM, BEA等其他人只要按照sun的文檔實現這些介面就可以了。如果sun發布的不是介面而是實現,如一個class,那麼ibm說我討厭sun的實現,它的效率太低下,而想改用自己的實現就不是那麼容易了。interface是抽象規範的好工具。
----------------------------------------------------------------------------------------------------------------------------
(聰明的一豬)
我在寫一個thread pool的時候,就發現Thread是個類而不是interface讓我很不爽。很多東西得繞個大彎子做,還做的不乾淨。
感覺這是java的一個缺點。
另外,同意趙明宇對繼承的批評。對繼承的過渡依賴(甚至認為OO就是繼承),來自於Smalltalk, Simula等語言的影響,以及認為繼承這種“inremental design”就可以自然地描述世界的天真。
----------------------------------------------------------------------------------------------------------------------------
(CSDN ,隨時隨地,想上就上)
舉個葷例說明介面的多態性
女人有個口子,叫私處
男人有根棒子,可以操作介面。同一個男人的棒子,可以操作不同的女人(只要是女人,有口子),但男人只知這個女人是女人,不知是張三妹,還是李四妹,這個時候,說女人是多態的(想想關燈後的多人成人派對,大家都不說話,抓住就上了)。對色狼來說,他只認有口子的人,不去挑張三妹還是李四妹。
對比:
男人:客戶,消費者
女人定義:有口子的都是女人
男人消費女人:有口子的都可以被消費
上面一再強調有口的就是女人,那麼母狗母豬母牛也有口子,是不是男人也或以消費它們呢?答案是肯定的,這樣,就不能推論說因為女人是人,母狗母豬母牛也是人,只能說,她(它)們是母的。這個母的就是介面。女人可以實現,母狗母豬母牛也可以實現,這就是角色的意思。
人可以用手製造工具,這是人的一個技能,因此,女人也可以製造工具,但母豬母狗就不行了,因此,人到女人是功能實現(都是人),母到女人,母狗母豬母牛是角色實現(都是女的)。
(鄭關西)
littlecpu(CSDN, 隨時隨地,想上就上。)
說的雖粗,但是經典!!!
從他的例子可以看出,男人、女人就是類,可以繼承,公豬、母豬也是類也可以繼承,
但是所有雌性的哺乳動物都實現了那麼個插座,所以所有雄性的哺乳動物都實現了插銷。
這樣生物才能繁衍,由雩都實現了插座、插銷的介面,這些哺乳動物就可以隨便找一個異性同類(class)來工作。
其實這裡面挺複雜,雌性還實現了另一個介面,生小孩,雖然都是一個口裡的,但實現的功能不同。後者又跟繼承扯上了關係。
那麼我們可以定義介面的兩個方法。一個是交配,一個是生小孩。
例如:
Ifemale i=new 女人();
i.生小孩();
一個孩子誕生;
Ifemale i=new 母豬();
i.生小孩();
一隻小豬誕生了;
這就實現了物件導向的多態。
(鄭關西)
抽象類別是用於組件內部的。
介面是用於組件外部的。
至於人和豬介面的具體實現,大家不用去管,那是上帝實現的。
----------------------------------------------------------------------------------------------------------------------------
(山不在高)
我也湊熱鬧,
上面大家講了很多介面的好處了。
不過我覺得那些都比較虛,
不是說每次寫東西先來個介面,然後實現就會有那樣的好處,
必須能真正抓住事物的本質,要不然,寫出來的介面只能是一遇到變化
就修改,一遇到麻煩就擴充,根本就成了累贅,因為一擴充,那擴充了這個介面的
類都要改,SWEAT。
理解介面,關鍵在於學會抽象,抽象是一個分析的過程,
說白了還是物件導向的分析問題,這個話就長了,也非我所長(不到家的人就
這樣,有時候好像明白點,可是卻說不出來,見笑了)。
一個朋友和我說,每個對象都是有生命力的,開始不明白,後來仔細想來很有道理。
----------------------------------------------------------------------------------------------------------------------------
(劉瑜江)
你可以試作把介面詳陳一個信封,信封上的地址,郵編,收件者姓名和信中的內容都相當於介面
中的提供的方法,這些方法是在從發件到郵局到收件者都採用的通用的標準,你只要把信封上的
各個元素填充完整(即介面實現)了,那麼這封信就能在很大範圍被識別和使用,當然你也可以在
信封的一個下角做一個符號(只有寄件者和指定的收件者能能理解),那麼你可以在一個信封實現時在信封上加一個元素,就行了.
(層級不高,理解可能有誤,請諫量)
----------------------------------------------------------------------------------------------------------------------------
(獨孤求敗)
我還是比較支援把介面理解為‘規範’或者‘標準’的觀點。介面就是一張紙,紙上嚴格的規定了許多條目。
truezerg(趙明宇)舉的例子沒有錯,但是這裡只體現了Interface的部分內涵,而且抽象類別也有這個功能。
我認為最好的例子還是 javawings(JavaWings) 舉的插座的例子,比如某個大樓的電源插座是三孔的,要使用時,所有的插頭,必須使用三孔
----------------------------------------------------------------------------------------------------------------------------
(狗賊)
在下剛開始學習介面的實際編寫,因為發現採用了好的物件導向設計方法後還是回存在細節上的疏忽和錯誤,例如類定義沒有太大問題,方法也如此,但在重載中發現我在不斷的加參數、以至於一個方法可能要被重載許多次,每一次都需編譯所有的調用,請問各位前輩,如果我定義這個調用的介面類後,在聲明時是否不用考慮參數或傳回值的一致性,只需方法名一致即可?或這個問題可以用抽象類別來解決?
(趙明宇)
不行,只考慮方法名不考慮參數類型和參數個數那介面的作用就沒有什麼了。你在重載中發現在不斷的加參數,以至於一個方法可能被重載了好多次,這說明你的介面定義的有問題。介面如果不滿足使用者的話,你在寫類的時候就想去重載已有的方法,加參數什麼的。即使你這樣做了,也沒有什麼用。 因為介面裡沒有後添加的方法。你一樣無法通過介面來使用。
在確定介面是否定義完成後你試著測試一個介面是否滿足了要求,然後再決定是否開始實現它
(狗賊)
謝謝 truezerg(趙明宇),您的意思是否可以這樣理解,在介面定義時必須對所有方法的重載情況都聲明,至於實現可以放到下一步,但規劃好這些方法是前提條件,否則表示類的抽象分析過程存在問題。
(趙明宇)
可以這麼說,如果你在實現介面的過程中經常發現少了一個方法,或是需要一個重載方法,然後你在到介面中添加上,然後再去實現它。 這就說明你介面定義時就沒有定義好。直到你去實現時才發現不滿足需求,於是你就去改。這樣就不好了。
我個人經驗,介面裡如果出現過多的重載方法很有可能也沒有設計好。一般介面體現對上一層的服務。服務一般是較穩定的,過多的重載方法,參數的不穩定性導制介面也比較脆弱。而且從服務的角度來說好像也不是非常必要。 只是我個人的想法。
----------------------------------------------------------------------------------------------------------------------------
(sagaman)
請問:象java.sql包裡面的那些介面的實現在哪裡?PreparedStatement對象的getConnection方法返回Connection對象,然後就可以直接使用該Connection對象的方法,並沒有像趙明宇大蝦所說的那樣應用。
另外,如何把一個介面的所有常量屬性都枚舉出來?例如,我把學曆的所有類別防到介面Education中,每個學曆作為Education介面一個屬性,我在某處需要全部列出,怎麼實現?是不把所有的學曆作為介面的一個數組類屬性?
(趙明宇)
to: sagaman(sagaman)
---------------------------------------------------
請問:象java.sql包裡面的那些介面的實現在哪裡?PreparedStatement對象的getConnection方法返回Connection對象,然後就可以直接使用該Connection對象的方法,並沒有像趙明宇大蝦所說的那樣應用。
---------------------------------------------------
事實上你用的不正是Connection的介面嗎? 而getConnection返回的正是一個實現了這個介面的具體的類的對象啊。 你說在哪裡實現的? 在你用的每個具體的資料庫的驅動程式裡。你做資料庫連接前一定會用到這句話吧。Class.forName(driver).newInstance(); 這就是在載入一個具體的資料庫驅動程式。
JDBC正是一個使用介面的極好的例子啊。 你只管用介面裡的方法就行了,不管你用什麼資料庫的驅動程式。 就像你只管用交通工具的駕駛方法不管你是在開什麼車。
(sagaman)
to 趙大:
第一個問題:如何隱藏介面的實現?例如你並不知道Resultset、Statement介面怎麼實現的,這些介面的實現對你來說是隱藏的。
(趙明宇)
隱藏介面的實現? 你知道Oracle的JDBC是怎麼實現的嗎? 我想大多數人都不關心這個吧。 但我們都用過它的驅動來做Oracle的資料庫應用, 你用的時候就是在用jdbc的介面,就是在對介面說話。 這是不是你所說的隱藏?
其實對介面的實現沒有必要非得隱藏。 也不必隱藏。
----------------------------------------------------------------------------------------------------------------------------
(火山)
說一點個人的理解。
介面是某種外在特性的描述。因此private,protect都是無效的。因為它不涉及具體的實現,因此變數定義也是無效的。
如interface 消化功能 {
public 吸收() ;
public 排泄() ;
}
類是某種實體規則的定義。在類中可以實現介面。
如class 人impliment消化功能 {
吃();
喝();
拉();
撒();
public 吸收() {
吃或喝
}
public 排泄() {
拉或撒
}
}
從代碼的角度來說,類繼承可以減少子類代碼,而介面的實現只會增加代碼。
最後,介面和介面之間是沒有繼承關係的,只有擴充關係
----------------------------------------------------------------------------------------------------------------------------
(魔之眼)
介面就是對客戶的承諾
----------------------------------------------------------------------------------------------------------------------------
(mozart2000)
其實用電腦DIY來比喻最直觀了
主板上那些插槽都是介面。
只要符合介面的規範,你可以隨意使用產品。
比如,你可以用DLink的網卡,也可以用3Com的網卡,
而整合網卡的主板,你就沒有替換的餘地了。
用介面來編程,可以提高軟體的擴充性。
----------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------