Henry的VB.NET之旅(十)—何時用介面
?????? Henry的VB.NET之旅(十)—何時用介面<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
??????????????????????????????????????? 韓睿?
大李沒告訴我介面與抽象類的區別,什麼時候用介面,什麼時候用實現繼承。弄得我中飯也沒吃好,老在琢磨這事,這不,一吃完飯,我就衝上樓,一個房間一個房間轉,到處找大李。
過了好一陣,這老哥才和幾個同事說說笑笑地回到辦公室,我立即走上前,半請半拉地把他拽到電腦旁。“大李哥,我實在想得頭暈,既然在VB.NET中介面有了這麼大的發展空間,在形式上與抽象類如此相似,那麼它們有什麼區別?什麼時候用介面呢?”
聽著我一連串的問題,大李微笑著搖搖頭,拍拍我的肩膀說:“小兄弟,不光是你弄不清,其實就是很有經驗的程式設計師也對什麼時候用介面,什麼時候用抽象類而頭痛咧。”
此話一出,我更是疑惑重重。不過反而安下心來,老鳥們都弄不清的問題,我不清楚也不必心中不安了。哈……
大李看著我忽憂忽喜的表情露出了一絲詫異,不過他沒有理會,繼續說:“但是這個問題我們還是有必要好好分析一下,讓我們更明白介面與抽象類的具體含義。”
“我們早說過,抽象類是一種不能例項化而必須從中繼承的類。抽象類可以完全實現,但更常見的是部分實現或者根本不實現,從而封裝繼承類的通用功能,它可以提供已實現的成員,因此,可以用抽象類確保特定數量的相同功能,但不能用介面這樣做。”
“介面是完全抽象的成員集合,它的成員都無法在介面定義時實現,我們可以將它看作是為操作定義合同,介面的實現完全留給開發者去做。它們之間的區別,如果認真分析,還是有不少的:在VB.NET中,類只能是從一個基類繼承,所以如果要使用抽象類為一組類提供多型性,這些類必須都是從那個類繼承的;介面就不一樣了,它不但可以用一個類或結構實現多個介面,一個介面還可以有多個實現。”
“也就是說,它們在提供多型性的問題上是有差別的?”我好象聽懂了點什麼。
“這是一個重要的區別,我們也可以從多型性的角度來考慮是要使用介面還是用抽象類。”大李同意了我的觀點,“如果預計要建立元件的多個版本,我們應該建立抽象類。這是因為,抽象類提供簡單易行的方法來控制元件版本。通過更新基類,所有繼承類都隨更改自動更新。這是好處,當然也是問題,對吧?(詳見前文《脆弱的基類》)另一方面,介面一旦建立就不能更改。如果需要介面的新版本,必須建立一個全新的介面。所以,如果建立的功能將在大範圍的全異物件間使用,則使用介面。”
我想了一下,接著大李的話說:“能不能這樣說,抽象類主要用於關係密切的物件,而介面最適合為不相關的類提供通用功能。”
大李對我伸出了大拇指:“不錯,小夥子悟性很好呀!你想,我上午跟你說,要建立控制元件,首先就是要對一些介面進行實現以讓系統能夠識別(詳見前文《介面》)。而各個控制元件之間的聯絡其實關聯性並不大。所以,它們的基礎大都是介面。但是,我們要注意一點,在元件設計時,如果要在元件的所有實現間提供通用的已實現功能,則使用抽象類。這是因為我們剛才說過的原因,抽象類允許部分實現類,而介面不包含任何成員的實現。”
“唔,明白了,它們之間的區別有點明白了。”我默默地點了點頭。
“另外,有個通用的設計思想,如果要設計小而簡練的功能塊,則使用介面。如果要設計大的功能單元,則使用抽象類。”大李又補充了一條建議。
“看來設計的問題還是蠻大的,一般來說,怎麼設計介面呢?”我接著問。
“為什麼你所看到的程式設計書籍也好,程式例程也好,極少有對介面的描述,而對類實現繼承的例子比比皆是?這就從一個側面給我們提了一個醒,如果使用適當,介面可以成為很有用的工具。但如果使用不當,它們會變得非常棘手,甚至妨礙有效的程式設計。介面的設計與使用其實是一項高明的藝術。”大李鄭重其事的說。
“藝術?”我驚叫了一聲。
“沒錯,藝術!”大李又加重了一下語氣,“通過介面與實現的方式,我們可以將同一型別的程式運用在不同的物件上面,而且不必修改原有類,相對子程式必須通過修改源程式程式碼才能夠達到重用的目的,介面與實現不僅是偉大的進步,也是境界極高的程式設計藝術。”
“哦,這倒是真的。”我回想起今天看到的介面的例程。
“但是,最大的問題還是集中在介面設計上。”大李接著說,“介面一旦被定義和接受,就必須保持不變,以保護為使用該介面而編寫的應用程式。介面釋出後,就不能對其進行更改。這是我們進行元件設計的一個重要原則,叫做‘介面不變性’。”
我點了點頭:“介面不變性,這個我可以理解了。”
“我已經反反覆覆強調過,建立一個介面就是在建立一個定義,介面定義釋出後則永遠不能更改。介面不變性,就是為了保護為使用介面而編寫的現有系統。當介面設計與需求有所出入,確認需要大幅度變更時,我們應該建立新的介面。一般命名方式是,新介面可以通過在原來的介面名稱後追加一個數字‘2’來命名,以顯示出它與現有介面的關係。然後通過介面繼承來建立新介面。”
“可是,如果需求分析得不好,豈不是會出現一大堆的派生介面了?”我不免有點顧慮。
“這是肯定的,而且過於頻繁地生成新介面,會因未使用的介面實現而使元件變得很龐大。有經驗的設計師,在充分分析需求後,設計出的介面往往很小且相互獨立,減少了效能問題發生的可能。”
“這種分解能力倒真的是藝術呀!”我不禁為之嘆服。
“當然,一般來說,我們會把確定哪些屬性和方法屬於介面的設計過程稱為‘介面分解’。基本準則是,應在一個介面中將緊密相關的幾個功能聚合為一組。功能太多會使介面不便於執行,而過於細分功能將導致額外的系統開銷並降低使用的簡易性。掌握分解的這個度的能力是需要不斷的在技術上進行磨鍊,以及在對每個專案深入分析與理解的基礎上才能得到的。”
“明白了。”我大聲地回答著,真希望自己能早一天成為介面設計大師。
大李笑著拍了拍我:“明白了就好。其實,與設計介面相比,建立大的實現繼承樹更容易出錯。”
“當然,”我腦海裡浮現起實現繼承的各個環節,“這是不是說,在某些時候適當使用介面還是很有益的。”
“看來你真的明白了,那你再來說一下,介面與類實現繼承相比,好處有什麼?”大李回過身開始找茶杯。
我低下頭,努力地轉動了一下腦子:“我試著說一下吧,總體而言,介面是一種非常有效的程式設計方法,它讓物件的定義與實現分離,從而可以在不破壞現有應用程式的情況下使物件得以完善與進化。介面消除了實現繼承的一個大問題,就是在對設計實施後再對其進行更改時很可能對程式碼產生破壞。即使實現繼承允許類從基類繼承實現,在類首次釋出時仍然會使我們不得不為設計做很多的抉擇。如果原有的設想不正確,並非總可以在以後的版本中對程式碼進行安全的更改。比如,我們定義了一個基類的方法,它需要一個 Integer 引數,而後來又確定該引數應該為 Long 資料型別。我們無法安全更改原始類,因為為從原始類派生的類所設計的應用程式可能無法進行正確編譯。這一問題會擴大化,因為單個基類會影響幾百個子類。”
“那用過載原始類並採用一個Long型別的引數,不就能解決這個問題了嗎?”大李提了個問題。
“這個麼?”我想了一下,“可是,這樣不一定能達到滿意的效果,因為一個派生類可能需要對採用整數的方法進行重寫,如果取 Long 資料型別的方法不能被重寫,該派生類可能無法正常執行。”
“那用介面怎麼做?”大李不依不撓地繼續問。
“辦法就是釋出接受新資料型別的更新介面。”我一下子就回答出來了。
“看來你已經掌握了介面操作的基本環節了。”大李的評語真讓我高興。“我再幫你總結一下,使用介面繼承而不用類繼承的主要原因有:在應用程式要求很多可能不相關的物件型別以提供某種功能的情況下,用介面適用性會更強;介面比基類更靈活,因為可以定義單個實現來實現多個介面;在無需從基類繼承實現的情況下,介面更好;在無法使用類繼承的情況下介面是很有用的。例如,結構無法從類繼承,但它們可以實現介面。”
我抿著嘴用力點了點頭,同時在心裡默默地記憶著大李所說的每一條準則。
“回去好好想想,多寫幾個小程式來練習一下,明天我們還要欣賞VB.NET提供的強大的可視繼承的表現呢。”
(未完待續)
---------------------------------------------------------------
宣告:本文版權與解釋權歸韓睿所有,如需轉載,請保留完整的內容及此宣告。
QQ: 18349592
E-Mail: henry7685@hotmail.com
請訪問本人專欄:http://www.csdn.net/develop/author/netauthor/Latitude/
相關文章
- Henry的VB.NET之旅(八)—介面
- Henry的VB.NET之旅(九)—介面繼承繼承
- Henry的VB.NET之旅(十二)—事件驅動事件
- Henry的VB.NET之旅(十一)—可視繼承繼承
- Henry的VB.NET之旅(十三)—標準事件處理程式事件
- Henry的VB.NET之旅(十五)—動態事件處理方法事件
- Henry的VB.NET之旅(十四)—動態關聯事件與處理程式事件
- Henry手記-VB.net WinForm中的FORM初探(二) (轉)ORM
- 何時用 struct?何時用 class?Struct
- VB.net中HOOK的應用(二) (轉)Hook
- 你的Linux進階之旅,終點是何處?Linux
- Java介面的例項應用:致敬我的偶像——何塞·穆里尼奧Java
- gRPC入門學習之旅(十)RPC
- 何時抽象抽象
- Solidity之旅(十)OOP-抽象合約SolidOOP抽象
- vb.net 介面POST方式傳引數提交返回值
- SAS介面與SCSI介面的硬碟有何區別硬碟
- 「譯」JavaScript 計時器之旅JavaScript
- 安卓之旅-時鐘開發安卓
- NoSQL“開山之作”Amazon DynamoDB的十年創新之旅SQL
- 瀏覽器的session何時消失?瀏覽器Session
- vue在不確定介面何時請求完的情況下,如何改變陣列Vue陣列
- 何時選擇敏捷?敏捷
- Win32彙編教程十 定時器的應用 (轉)Win32定時器
- 何時入場才是好時機
- Flutter入門進階之旅(十)Dialog&ToastFlutterAST
- BeanPostProcessor 是在何時介入 Bean 建立的?Bean
- [譯]JS箭頭函式三連問:為何用、怎麼用、何時用JS函式
- 我的PIM之旅(3) - PIM雲應用
- 大衛談學習4:為何你會一年經驗用十年?
- Sql Server之旅——第十站 看看DML操作對索引的影響SQLServer索引
- IPFS何時能取代HTTP?HTTP
- 何時使用Entity或DTO
- 瞭解何時使用RabbitMQMQ
- 何時 NGINX 將取代 Apache?NginxApache
- C# 給PDF簽名時新增時間戳的2種方法(附VB.NET程式碼)C#時間戳
- 在你的VB.NET應用程式中使用多執行緒 (轉)執行緒
- 用JScript實現VB.Net,C#的[委託Delegate]: (轉)JSC#