周立功教授數年之心血之作《程序設計與數據結構》以及《面向AMetal框架與接口的編程(上)》,電子版已無償性分享到電子工程師與高校群體,。書本內容公開后,在電子行業掀起一片學習熱潮。經周立功教授授權,本公眾號特對《程序設計與數據結構》一書內容進行連載,愿共勉之。
第四章為面向對象編程,本文為 4.5框架與重用。
重用不僅限于軟件,比如,貝多芬在他的66號作品中,就重用了另一個偉大作曲家莫扎特的音樂。他從莫扎特的歌曲《魔笛》第22場中,借用了詠嘆調“一個女朋友”,然后在該詠嘆調中為鋼琴師配樂的大提琴家寫了一連串7個變奏。
代碼重用的問題與所有的設計方法一樣,代碼的可用性和可重用性取決于它是如何設計和實現的。雖然代碼重用并不是OO設計所專有的,但OO方法確實提供了一些機制,有利于可重用代碼的開發。
>>> 4.5.1 框架
框架被定義為“一組相互協作的類,形成某類軟件的一個可復用設計。框架將設計劃分為一組抽象類,并定義它們各自的職責和相互之間的協作,以此指導體系結構級的設計,開發者通過繼承框架中的類和組合其實例定制該框架以生成特定的應用。”
從某種意義上來說,框架是可以通過某種回調機制進行擴展的軟件系統或子系統的半成品。也就是說,首先,框架是半成品,這是它和其它所有軟件組件的本質區別。而某種回調機制,通常面向過程編程使用函數指針作為參數實現回調機制,比如,冒泡排序和快速排序中的compare的形參就是一個函數指針,開發者只需知道自己實現特定的比較函數即可。而面向對象框架的組成部分包括具體類、抽象類和接口,使用抽象方法——多態支持回調機制實現逆向工程。
顯然,創建可重用代碼的一種方法就是創建框架,框架規定應用的體系結構,它定義了整體結構,類和對象的分割,各部分的主要職責,類和對象如何協作,以及控制流程。框架預定義了這些設計參數,便于設計者聚焦于應用本身的特定細節。但框架使開發程序變得更加容易,因此程序設計需要的許多能力都來源于大量可用的框架。
與代碼重用緊密相關的一個概念是標準化,有時也稱為即插即用,框架思想圍繞的就是這些即插即用和重用原則。在GUI應用程序中,用戶界面視為視圖。而實際上在MVC框架中,視圖是一個接口,一個抽象的概念,因此視圖可能是一個用戶界面,也可能是一個終端,但只要實現了update接口的類,都可以將它們看作視圖,從而全面擴展了MVC框架的應用范圍。因為無論怎么改變,MVC框架的模型與視圖始終是不變的,可變的是具體模型和具體視圖。
以溫控器為例,通過傳感器的溫度檢測是具體模型,而監聽傳感器的LED、數碼管和蜂鳴器是具體視圖。當溫度達到或超過上限值時,則數碼管更新顯示,LED持續閃爍,蜂鳴器持續報警。根據開閉原則,可以繼續重用MVC框架的抽象模型與視圖。如果后續只要開發與溫度檢測相關的系列產品,就可以重用該溫度檢測模型。
如果設計的系統必須使用不可移植的代碼,那么應該將這些代碼抽象到類中,通過抽象將這些不可移植的代碼隔離到各自的類中。比如,針對基于M0+、M3、M4、ARM9、A7、A8內核的ARM和DSP的不兼容性,周立功單片機公司開發的AWetal和AWorks就是一個將所有的接口、外圍器件和組件全部都實現歸一化,且與MCU和OS完全無關的框架,從而實現了“一次編程、終生使用、跨平臺”,詳見《面向接口的編程》系列圖書。
由此可見,由于軟件的整體框架結構是一樣的,因此用戶不必學習新的框架;其次,開發人員只要遵循框架文檔提供的類或類庫的公共接口,以及應用編程接口API等規則,就可以充分利用原有的代碼。
>>> 4.5.2 契約
抽象類與接口是實現代碼重用的強大機制,為一個重要的概念“契約”奠定了基礎。那什么是契約?契約是兩方或多方完成或不完成某個指定工作所達成的協議——這是一個由法律保證的協定,因此契約是要求開發人員遵守應用編程接口規范所需的機制。
一般來說,API就是指一個框架,開發人員使用API時,必須遵守框架所定義的規則。比如,方法名和參數個數等。如果沒有強制性的措施,一些比較差勁的程序員可能會私下編寫他自己的代碼,而不使用框架提供的規范。如果人們總是忽視或不考慮標準化,那么標準也就沒有什么意義了。
面向對象的設計一個重要的目標就是將接口從實現中分離出來,一個類的接口提供了它的外部視圖,記錄了所有相關對象的共同屬性和行為。其強調的是抽象,隱藏了它的屬性和行為的秘密,不需要提供其內部關于該操作的實現(結構)。
接口在很大程度上可以認為是類的外部視圖的設計者和類的內部實現的實現者之間的一種“契約”,同時也是需要(使用)該接口的類(如調用該接口所提供的操作)和提供該接口的類之間的一種約定。即將一個較大問題的不同功能通過子契約被分解為小問題,沒有別的情況比在設計類時更能體現這種思想。一個單獨的對象就是一個具體的實體,在系統中扮演某個角色。
將接口從實現中分離出來是通過抽象類來實現的,抽象類包含一個或多個沒有提供任何具體實現的方法。Validator之所以是一個抽象類,因為無法對它實例化。比如:
這與契約有什么關系?首先,希望所有與視圖對應的顯示函數都使用相同的語法調用,比如,實現的每一種視圖都包含一個名為validate的方法。其次,每個類都要對自己的動作負責,因此類不僅要提供相應的方法,還必須提供它自己的實現代碼。比如:
由此可見,采用這種方式,就有了一個真正多態的Validator框架。系統中每個與視圖對應的顯示函數都可以調用validaate方法,而調用每個與視圖對應的顯示函數時都會得到不同的結果。實際上,向一個對象發送一個消息時,會根據對象的不同而產生不同的響應,這正是多態的根本所在。
>>> 4.5.3 建立契約
定義契約的規則是通過抽象類提供一個未實現的方法,當設計一個子類實現某個契約時,它必須為父類中未實現的方法提供實現,因為契約帶來的好處可以標準化代碼。如果開發人員不遵循契約設計類,那么使用類的所有人都必須查看文檔。比如:
雖然這樣做也能夠實現范圍值和奇偶校驗功能,但不符合契約。因為面向對象的主要優勢之一是可以重用類,重用的高層次的抽象接口比高度具體的接口更有用。
>>> 4.5.4 教條的危害
著名的語言專家王垠認為,“很多編程的人喜歡鼓吹各種各樣的原則,并將那些所謂的原則奉為教條或者秘方。以為兢兢業業地遵循這些原則,空喊幾句口號,就可以寫出好的代碼。同時對違反這些原則的人嗤之以鼻——你不知道,不遵循或藐視這些原則,那么你就是菜鳥。”因此不要盲目地迷信各種各樣的原則,比如,DRY原則(Don’t Repeat Yourself,不要重復你自己)在實際的工程中帶來了各種各樣的問題,卻經常被忽視。DRY原則說,如果你發現重復的代碼,就提取它們為一個父類。 然而“避免重復”并不等于“抽象”,有時候適當的重復代碼是有好處的。
代碼的“抽象”和它的“可讀性”,其實是相互矛盾的關系。適度的抽象和避免重復可以提高代碼的可讀性,如果你盡“一切可能”從代碼里提取共性,甚至將一些微不足道的“共性”也提出“共享”,反而破壞了程序的可閱讀性。如果盲目地將以下代碼:
修改為:
當你看到TypeA和TypeB的定義時,再也不能一目了然地看到int a。其實完全沒有必要提取這中無關緊要的共性,造出一個新的父類,因為可見性是程序員產生直覺的關鍵。奉行DRY原則的人存在的問題,在于是他們隨時都在試圖發現“將來可能重用”的代碼,而不是等到真的出現重復的時候再去做抽象。
抽象思想的關鍵在于“發現兩個東西是一樣的”,然而很多時候,開始時覺得兩個東西是一回事,最后發現它們其實只是膚淺的相似,而本質完全不同。同一個int a,其實可以表示很多種風馬牛不及的性質。你看到都是int a就提出來為父類,反而讓程序的概念變得混亂。有些東西開始時貌似同類,當添加了新的邏輯之后,發現它們的用途開始特殊化了。因此過早地提取共性,反而捆住了手腳,為了所謂的“一致性”,而重復一些沒用的東西。這樣的一致性,其實還不如針對每種情況分別做特殊處理。
防止過早抽象的方法其實很簡單,它的名字叫做“等待”。其實就算你不重用代碼,也不會影響程序的準確性和可讀性,時間能夠告訴你一切。如果你發現自己仿佛正在重復以前寫過代碼,請先不要停下來,堅持將這段重復的代碼寫完。如果你不將它寫出來,你將無法準確地發現重復的代碼,因為它們很有可能到最后其實是不一樣的。
我們應該避免沒有實際效果的抽象,如果代碼才重復了兩次,就開始提取共性,也許到最后會發現,這個模板總共也就只用了兩次。只重復了兩次的代碼,大部分時候是不值得為它提取模板的。因為模板本身也是代碼,而且抽象思考本身是需要一定代價的。所以最后總的開銷,也許還不如就讓那兩段重復的代碼待在里面。
而優秀的程序員等到事實證明重用一定會帶來好處時,才會開始提取共性進行抽象。實踐經驗證明,每一次積極地尋找抽象,最后的結果都是制造一些不必要的框架,搞得自己的代碼自己都看不懂。如果過度地強調DRY,強調代碼的“重用”,隨時隨地想著抽象,結果就會被這些抽象攪混了頭腦,bug百出寸步難行。如果你不能寫出“可用”的代碼,又何談“可重用”的代碼呢?
其實人們寫程序本來自然而然就會在合適的時候進行抽象避免重復,因此千萬不要迷信某個大師或專家起了一個DRY這樣的名字,就將我們繞進去了,反而使我們喪失了透過現象看本質的思維能力。
回頭來看,里氏替換原則也沒有什么特別之處,無論是否有人提出這樣的原則,子類對象與父類對象的地址值相等且類型相同,這是在語言層面天生就支持的行為。比如,雖然&rangeValidator與&rangeValidator.isa的類型不同,但它們的值相等。由于&rangeValidator.isa與pThis不僅類型相同它們的值相等,因此子類對象替換父類對象也就成為了事實。
當你看透了問題的本質之后,也就具備了洞穿一切的能力。顯然里氏替換原則只是套了一個馬甲而已,因此人們常說,“盡信書不如無書”,由此可見不無道理。
-
程序設計
+關注
關注
3文章
261瀏覽量
30391
原文標題:周立功:盡信書不如無書
文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論