見山只是山 見水只是水——提升對繼承的認識

來源:互聯網
上載者:User

見山只是山 見水只是水——提升對繼承的認識

溫 昱

封裝、繼承、多態是OO的三大特性,由此可見繼承思想的重要性。但是,不少人對繼承的理解過多地局限在OOP層面,從而限制了繼承思想在OOD層面的巨大作用。筆者認為,軟體工程師應該不斷提升對OO思想的認識層面,加強實際開發能力。

本文站在OOD的角度,將繼承看成實現OOD的強大手段,通過具體例子,說明針對介面編程(Program To An Interface)、混入類(Mix In Class)、基於角色的設計(Role-based Design)這三個與繼承緊密相關的著名OOD技巧。

 

 一、從一則禪師語錄說起

 

《五燈會元》卷十七中,有一則青原惟信禪師的語錄:“老僧三十年前未參禪時,見山是山,見水是水。及至後來親見知識,有個入處,見山不是山,見水不是水。而今得個休歇處,依前見山只是山,見水只是水。”

禪師高論,頗具哲理,講的是悟道的過程。其實,領悟OOD之道的過程又何嘗不是如此呢。

 

1、見繼承是繼承——程式員境界

初學OOP的人,大多處在“見繼承是繼承”的層面,最關心的是類的文法、類的成員變數、類的成員函數等這些實現層的東西。這是程式員境界。

 

2、見繼承不是繼承——成長境界

開始研習OOD之時,又往往跳到另一個極端,只關心設計,而無心(也可能是無力)關心實現,處在所謂“見繼承不是繼承”的層面。在這個階段的人,腦中的興奮點是“設計”,是職責分配、介面設計、可重用性、可擴充性、耦合度、彙總度等這些設計層的概念。這是成長境界。

 

3、見繼承只是繼承——設計師境界

學通OOD之後,會達到“見繼承只是繼承”的層面。一個“只”字,體現了繼承背後的“設計理念”才是該境界的要害。但是,這個階段和第二階段不同,第二階段是一味的否定,而本階段是否定之否定,把OOP層面的繼承機制看成用來實現特定OOD的手段加以利用。這是設計師境界。

  

二、從OOD層面認識繼承

 

在OOP層面,除了類、成員變數、成員函數這些最基本的概念,最重要的就是代碼重用和名字空間的可見度了。而OOD層面,最基本的概念是類、職責、狀態、角色這些更抽象一級的概念,及其相關的耦合度、彙總度、可重用性、可擴充性、可維護性等。可見,雖然OOD最終要依賴OOP作為實現手段,但顯然OOD和OOP並非在同一抽象級上,有不同的概念體系和思維方式。

再說繼承。單純從OOP層面看,繼承是一個通過複用父類功能而擴充應用功能的基本機制,它允許你根據舊的類快速定義新的類;還有些人用繼承僅為了擷取名字空間的可訪問性。但是,從OOD層面看,繼承可以演變出 “Is-A”、“Plays Role Of”等抽象的設計概念。因此,擔任設計師角色的人如果自己還限制在OOP的層面,“設計乏術”的局面是不可避免的。總之,提升對繼承的認識,對活用介面繼承和實現繼承這兩種繼承機制來實現OOD意圖非常重要。

與繼承相關的OOD技巧有很多,本文僅討論比針對介面編程、混入類、基於角色的設計這三種技巧,下圖展示了它們和繼承的關係。

  

三、針對介面編程——隔離變化

 

1、相關理論

耦合是依賴的同義字,被定義為“兩個元素之間的一種關係,其中一個元素變化,導致另一個元素變化”。抽象耦合被定義為“若類A維護一個指向抽象類別B的引用,則稱類A抽象耦合於B”。

依賴性倒置原則(Dependency Inversion Principle)形式化了抽象耦合的概念,明確表述了應該“依賴於抽象類別,不要依賴於具體類”。

針對介面編程遵守上述原則,從而在很大程度上阻止了變化波及範圍的擴大,有效地隔離了變化,有助於增強系統的可重用性和可擴充性。

 

2、針對介面編程舉例——用於體繫結構設計

根據經典的Coad的OOD理論,一個項目通常包含四個層:使用者介面層、問題領域層、資料管理層、系統互動層,如下圖所示。

將體繫結構劃分為層的一個很大好處是,這些層形成了開發小組的自然分界——每層的開發人員所需要的技巧是不同的。使用者介面層的開發小組需要瞭解將使用的使用者介面工具包;資料管理層的開發小組需要熟悉相關的資料庫、持久工具或者使用的檔案系統;系統互動層的開發小組需要瞭解通訊協議和用到的中介軟體產品;問題領域層的開發小組不需要瞭解這些知識,他們需要最深的領域知識,以及用到的相關分布對象或組件技術。

但是,要真正使得各個開發小組最大限度地獨立開發,還需要一個穩定的體繫結構設計做保證才行,其設計的核心思想是:問題領域層“不依賴於”其他任何層,而其他任何層“只依賴於”問題領域層。如下圖所示。

該體繫結構設計的實現,極為重要的一點,就是要使用針對介面編程的技巧。以系統互動層對問題領域層的單向依賴為例:

Ø         如果系統互動層要調用問題領域層的操作,直接調用即可。

Ø         如果問題領域層要調用系統互動層的操作,需要由問題領域小組定義一個通用的抽象介面,通過針對介面編程調用這個抽象介面;而系統互動小組通過介面繼承機制,定義抽象介面的子類,該子類完成抽象介面的具體實現。

筆者曾有一個項目,該系統需要即時地將本系統的資料變化,通知遠端的另一個系統。相關設計如下圖所示。在問題領域層,僅包含了一個抽象介面CChangeReporter,而並不關心CChangeReporter的具體實現。系統互動層擁有選擇具體實現方法的自由,比如CSoapChangeReporter是用SOAP通訊協議實現的CChangeReporter, CTcpChangeReporter是用TCP協議實現的CChangeReporter。而且假設由於技術的或商業的原因,將來需要同時支援多種通訊協議,也比較容易。

 

3、針對介面編程舉例——用於類設計

筆者曾在《運用設計模式設計MIME編碼類別》一文中,詳述了如何使用原則模式來設計一個可重用、易擴充的MIME類層次,其中抽象介面類CMimeAlgo起到了至關重要的作用,現簡述如下。

使用者通過CMimeString使用MIME編碼的功能,CMimeString允許使用者在運行過程中動態配置MIME編碼的具體演算法;具體MIME編碼演算法由CMimeAlgo類層次提供,具體的CMimeAlgo子類的執行個體化是由CMimeString根據使用者的配置動態完成的;要增加新的MIME編碼演算法,只需實現新的CMimeAlgo子類,並簡單擴充CMimeString的動態執行個體化代碼即可。如下圖所示。

 

四、混入類——更好的重用性

 

1、相關理論

混入類被定義為“一種被設計為通過繼承與其他類結合的類”,它給其他類提供可選擇的介面或功能。

從實現上講,混入類要求多繼承;混入類通常是抽象類別,不能執行個體化。

混入類的作用在於:它不僅可以提高功能的重用性,減小代碼冗餘;而且還可以使相關的“行為”集中在一個類中,而不是分布到多個類中,避免了所謂的“代碼分散”和“代碼交織”問題,提高了可維護性。

 

2、混入類舉例

來看一個具體項目。在一個信用卡客戶服務系統項目中,要求能夠以多種方式發送多種資訊給使用者,並能夠適應未來業務的發展變化。

當前系統需要支援的發送方式:

Ø         列印(並郵寄)

Ø         Email

Ø         傳真

可預見的未來要支援的發送方式:

Ø         手機簡訊

Ø         PDA訊息

當前系統需要支援的待發送資訊:

Ø         信用卡對賬單

Ø         信用卡透支催收單

可預見的未來要支援的待發送資訊:

Ø         信用卡新業務宣傳單

Ø         信用卡促銷活動宣傳單

下面是一些設計考慮。一種發送方式要支援多種待發送資訊,我們希望發送功能有很好的可重用性;為了方便未來加入對新的發送方式和發送資訊的支援,設計必須具有良好的可擴充性。相關設計如下圖所示。其中採用了混入類的OOD技巧,用一個CSendableDoc作為混入類,支援發送功能的重用;CSendalbeDoc還採用了策略模式支援發送方式的擴充。

 

 

五、基於角色的設計——使用角色群組裝協作

 

1、相關理論

協作被定義為“多個對象為了完成某種目標而進行的互動”。角色被定義為“特定協作中的對象的抽象”,它“僅定義了對象特徵的一個對某協作有意義的子集”。協作和角色的概念和現實世界很接近,比如下圖中,Jane教授扮演三個角色——母親、妻子、教授。

介面分離原則(Interface Separation Principle)信奉“多個專用介面優於一個單一的通用介面”的思想,因為“任何介面都應當具有高內聚性”,以便“保證實現該介面的類的執行個體對象可以只呈現為單一的角色”。

基於角色的設計的意義在於:我們很容易通過已有角色的組合來構造新的協作,以完成新的功能。而且,從UML類圖可以很自然地匯出基於角色的設計方案,例如:

 

從上面的類圖很自然地匯出下面的設計:

2、基於角色的設計舉例

比如,待開發的一個系統,其後台資料來源可能是關聯式資料庫、一般的檔案、還可能是另一個私人資料庫。既然介面可以隔離變化,我們可以定義一個單一的介面,為所有的資料客戶類提供服務。如下圖所示。

但是,上面的設計違背了基於角色的設計思想,根本不能保證“實現該介面的類的執行個體對象可以只呈現為單一的角色”,這會帶來一些問題。比如,有一個資料客戶類,不需要插入、更新等功能,而僅僅需要對資料進行讀操作,這時顯然一個提供“讀”服務的“角色”是最合理的設計,但CRowSetManager卻是如此之“寬”的一個介面。最終,我們可以這樣來改進設計,如下圖所示。

 

參考文獻:

《設計模式》 Erich Gamma等著 李英軍等譯

《重構——改善既有代碼的設計(影印版)》 Martin Fowler

《UML物件導向設計基礎》 Meilir Page-Jones著 包曉露等譯

《Java設計:對象、UML和過程》 Kirk Knoernschild著 羅英偉 汪小林譯

《特徵驅動開發方法原理與實踐》Stephen R. Palmer, John M. Felsing著 熊煥宇等譯

《Object-oriented programming: Role-based design》 W.McUmber 來自網上的投影片

《Role = Interface: A Merger of Concepts》 Friedrich Steimann 來自JOOP

《運用設計模式設計MIME編碼類別》 溫昱 《CSDN開發高手》第1期

以及來自www.objectmentor.com的多篇文章 

作者簡介:

溫昱,架構設計師,資深諮詢顧問,松耦合空間(http://lcspace.nease.net)創辦人。擅長物件導向、架構和架構設計,對設計模式、UML和軟體工程有深入研究。可以通過wenyu@china.com和作者聯絡。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.