1. 程式人生 > >什麼時候用抽象類,什麼時候用介面

什麼時候用抽象類,什麼時候用介面

“介面是完全抽象的成員集合,它的成員都無法在介面定義時實現,我們可以將它看作是為操作定義合同,介面的實現完全留給開發者去做。它們之間的區別,如果認真分析,還是有不少的:在JAVA中,類只能是從一個基類繼承,所以如果要使用抽象類為一組類提供多型性,這些類必須都是從那個類繼承的;介面就不一樣了,它不但可以用一個類或結構實現多個介面,一個介面還可以有多個實現。”

       抽象類是一種不能例項化而必須從中繼承的類。抽象類可以完全實現,但更常見的是部分實現或者根本不實現,從而封裝繼承類的通用功能,它可以提供已實現的成員,因此,可以用抽象類確保特定數量的相同功能,但不能用介面這樣做。 “也就是說,它們在提供多型性的問題上是有差別的?”我好象聽懂了點什麼。
“如果預計要建立元件的多個版本,我們應該建立抽象類。這是因為,抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動更新。這是好處,當然也是問題.另一方面,介面一旦建立就不能更改。如果需要介面的新版本,必須建立一個全新的介面。所以,如果建立的功能將在大範圍的全異物件間使用,則使用介面。”------:“能不能這樣說,抽象類主要用於關係密切的物件,而介面最適合為不相關的類提供通用功能。” 我上午跟你說,要建立控制元件,首先就是要對一些介面進行實現以讓系統能夠識別(詳見前文《介面》)。而各個控制元件之間的聯絡其實關聯性並不大。所以,它們的基礎大都是介面。但是,我們要注意一點,在元件設計時,如果要在元件的所有實現間提供通用的已實現功能,則使用抽象類。這是因為我們剛才說過的原因,抽象類允許部分實現類,而介面不包含任何成員的實現。”

“唔,明白了,它們之間的區別有點明白了。”我默默地點了點頭。

       “另外,有個通用的設計思想,如果要設計小而簡練的功能塊,則使用介面。如果要設計大的功能單元,則使用抽象類。” “看來設計的問題還是蠻大的,一般來說,怎麼設計介面呢?”我接著問。

“為什麼你所看到的程式設計書籍也好,程式例程也好,極少有對介面的描述,而對類實現繼承的例子比比皆是?這就從一個側面給我們提了一個醒,如果使用適當,介面可以成為很有用的工具。但如果使用不當,它們會變得非常棘手,甚至妨礙有效的程式設計。介面的設計與使用其實是一項高明的藝術。”

,“通過介面與實現的方式,我們可以將同一型別的程式運用在不同的物件上面,而且不必修改原有類,相對子程式必須通過修改源程式程式碼才能夠達到重用的目的,介面與實現不僅是偉大的進步,也是境界極高的程式設計藝術。”

 

“但是,最大的問題還是集中在介面設計上。介面一旦被定義和接受,就必須保持不變,以保護為使用該介面而編寫的應用程式。介面釋出後,就不能對其進行更改。這是我們進行元件設計的一個重要原則,叫做‘介面不變性’。”

“我已經反反覆覆強調過,建立一個介面就是在建立一個定義,介面定義釋出後則永遠不能更改。介面不變性,就是為了保護為使用介面而編寫的現有系統。當介面設計與需求有所出入,確認需要大幅度變更時,我們應該建立新的介面。一般命名方式是,新介面可以通過在原來的介面名稱後追加一個數字‘2’來命名,以顯示出它與現有介面的關係。然後通過介面繼承來建立新介面。”

  “可是,如果需求分析得不好,豈不是會出現一大堆的派生介面了?”我不免有點顧慮。

“這是肯定的,而且過於頻繁地生成新介面,會因未使用的介面實現而使元件變得很龐大。有經驗的設計師,在充分分析需求後,設計出的介面往往很小且相互獨立,減少了效能問題發生的可能。”

“這種分解能力倒真的是藝術呀!”我不禁為之嘆服。

“當然,一般來說,我們會把確定哪些屬性和方法屬於介面的設計過程稱為‘介面分解’。基本準則是,應在一個介面中將緊密相關的幾個功能聚合為一組。功能太多會使介面不便於執行,而過於細分功能將導致額外的系統開銷並降低使用的簡易性。掌握分解的這個度的能力是需要不斷的在技術上進行磨鍊,以及在對每個專案深入分析與理解的基礎上才能得到的。”

介面與類實現繼承相比,好處有什麼?”:“我試著說一下吧,總體而言,介面是一種非常有效的程式設計方法,它讓物件的定義與實現分離,從而可以在不破壞現有應用程式的情況下使物件得以完善與進化。介面消除了實現繼承的一個大問題,就是在對設計實施後再對其進行更改時很可能對程式碼產生破壞。即使實現繼承允許類從基類繼承實現,在類首次釋出時仍然會使我們不得不為設計做很多的抉擇。如果原有的設想不正確,並非總可以在以後的版本中對程式碼進行安全的更改。比如,我們定義了一個基類的方法,它需要一個 Integer 引數,而後來又確定該引數應該為 Long 資料型別。我們無法安全更改原始類,因為為從原始類派生的類所設計的應用程式可能無法進行正確編譯。這一問題會擴大化,因為單個基類會影響幾百個子類。”

“那用過載原始類並採用一個Long型別的引數,不就能解決這個問題了嗎?”大李提了個問題。

“這個麼?”我想了一下,“可是,這樣不一定能達到滿意的效果,因為一個派生類可能需要對採用整數的方法進行重寫,如果取 Long資料型別的方法不能被重寫,該派生類可能無法正常執行。”

“那用介面怎麼做?”大李不依不撓地繼續問。

“辦法就是釋出接受新資料型別的更新介面。”我一下子就回答出來了。

“看來你已經掌握了介面操作的基本環節了。”大李的評語真讓我高興。“我再幫你總結一下,使用介面繼承而不用類繼承的主要原因有:在應用程式要求很多可能不相關的物件型別以提供某種功能的情況下,用介面適用性會更強;介面比基類更靈活,因為可以定義單個實現來實現多個介面;在無需從基類繼承實現的情況下,介面更好;在無法使用類繼承的情況下介面是很有用的。例如,結構無法從類繼承,但它們可以實現介面。”