<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    冒號和他的學生們(連載27)——接口服務

     

    冒號和他的學生們

     

    27.接口服務

    律己宜嚴,待人宜寬                                          ——《洪應明·菜根譚》

     

    嘆號幡然反省:“以前我們做OOP編程時,總是專注于如何利用其他類來解決問題,而較少考慮自己設計的類對其他類的影響。”

    引號翻開以前的筆記:“前面提過,OOP的世界是民主制的,所有對象都是獨立而平等的公民,有權利尋求服務,也有義務提供服務。看來我們是光惦著權利而忘了義務了。”

    冒號繼而提出:“作為服務的提供者,最重要的是講誠信。首先,服務要有可靠性,不能陽奉陰違——即接口必須履行它的承諾;其次,服務要有穩定性,不能朝令夕改——即接口一經公開,不得隨意變更。”

    句號迅即領悟:“從抽象的角度看,服務的可靠性保證了規范抽象,服務的穩定性保證了數據抽象。”

    “孺子可教也!”冒號喜贊道,“相比而言,前者更為重要,但遺憾的是,只有后者才有法律保障——如果接口被廢棄或其簽名發生變化,固然會牽連客戶,至少還可通過編譯器來發現和修改;而規范只是語義上的契約,沒有語法上的約束,不在編譯器的監管范圍之內。”

    引號插言:“編譯器管不著,那只有靠單元測試了。”

    “這正是單元測試的主要目的。”冒號很認同,“此外,高質量的服務還要有純粹性完備性Unix有一個哲學:‘一個程序只做一件事,但要做好’。用在OOP上,則是:‘一個類只提供一套服務,但要完善’。譬如,同為手機,老式的大哥大提供的服務是純粹的,現代的智能手機則不是——除了打電話,還能攝像、聽音樂、打游戲、上網等等,完全是手機與掌上電腦的結合體。又如,同為通訊工具,手機提供的服務是完備的,而BP機提供的服務是不完備的——只能接收信息,不能發送信息。”

    嘆號搖頭晃腦:“提供的服務過多則不純粹,過少則不完備。如此設計出的類是不是要達到‘增一分則肥,減一分則瘦’的美人標準啊?”

     “編程畢竟是門實踐活兒,完美無缺的設計如夢中佳人,可以追求卻難以企及。”冒號笑了笑,“其實關鍵不在于服務數量的多寡,而在于服務的一致性和關聯性。連貫一致的服務是良好的抽象與封裝的結果,同時也是‘高聚合、低耦合’的保證。”

    “作一個服務的提供者真不容易啊。”問號嘆道,“那么,作為服務的享受者有什么講究嗎?”

    逗號鼻腔里發出共鳴聲:“哈,享受服務還需要講究嗎?”

    “當然有。”冒號斷然道,“作為服務的享受者,最重要的是講規矩。享受人家的服務,自然得按人家的規則,否則服務將得不到保障。比如,你可以在超市的貨架上任意選取商品,但不能偷偷溜進貨艙去取。”

    逗號辯道:“可貨艙想進也進不去啊。正如合適的封裝,是禁止客人進入私有接口的。”

    冒號作一引喻:“我們不妨這么假設,貨艙的正門掛著‘非工作人員莫入’的牌子。但是你偶然發現,通往洗手間的過道盡頭正好是貨艙的后門,既沒有上鎖,也沒有掛牌。請問你會不會大搖大擺地走進去?”

    逗號啞口無言。

    冒號循循善誘:“超市的工作人員也許不該為圖方便而開放后門,但那是管理者的事,作為客戶顯然不應乘虛而入。商品從貨艙到貨架之前可能會有裝箱、拆箱、條碼打印、條碼掃描等操作,客戶直接從貨艙拿貨無疑將破壞這種程序,于人于己皆無益處。同樣地,作為他人代碼的客戶,就應按他人所設計的方式去重用,如此才能保證預期的效果。至于他人代碼是否有效杜絕了一切可能的漏洞,那是監管軟件質量的負責人的職責。”

    引號表示理解:“這就好比客戶購買了一款產品,卻不按使用說明書進行操作,由此而引起的一切后果,廠家概不負責。”

    “就是這個理兒。”冒號輕錘桌面,“當然事物是一分為二的。生活中有一個司空見慣的現象:許多行人跨越護欄、橫穿馬路。一方面,行人應該遵守交通規則,不應破壞道路的‘封裝性’。另一方面,有些交通設計者沒有‘以人為本’的客戶意識,為行人提供的斑馬線、天橋或隧道之間相距過遠。從客觀上說,不夠完備的服務是導致行人違規的一大誘因。”

    此言顯見深得人心——幾乎人人都當過道路封裝的破壞分子。

    冒號接著問:“提個問題:當你們在使用一個類或其中的某個方法時,對其用法存疑,即使閱讀注釋文檔也無濟于事,怎么辦?”

    嘆號順嘴說道:“看源代碼唄。”

    “看源代碼是一種很好的學習和借鑒他人的方式,但不宜作為用法參考。”冒號否定道,“且不說源代碼有可能無法獲取,既便能夠,從中提煉出的用法也不一定可靠,更何況具體實現隨時可能變化。再打個比方,如果你不清楚如何設置一個鬧鐘,應該去看看說明書。如果說明書仍不解決問題,最好詢問廠家,而不是揭開鬧鐘的后蓋去研究它的運行機制,即使你真是個鐘表行家。”

    “所以應該直接咨詢代碼的作者。”逗號發現,過早搶答往往會掉入老冒的陷阱。這回學乖了,等嘆號落坑后才胸有成竹地應招。

    “方向正確!”冒號肯定后再次考問,“對方應以何種方式回答?”

    “可以口頭,也可以書面啊。”逗號答畢,隱隱覺得還是著了道。

    果然,冒號搖搖頭:“正確的做法是,對方應通過改進并提交的文檔來解釋。該過程可多次循環,直至問題解決。只有這樣,主客雙方的代碼維護者——包括當前的和將來的——才能真正受益。”

    問號深究:“但假如無法聯系到原作者呢?比如包括JDK庫在內的軟件?”

    冒號回答:“除了盜版的商業軟件,都應該能聯系到原作者。當然,如果與作者使用的不是同一源碼控制庫,上述做法也是可以變通的。好在無論是JDK庫,還是正規的第三方軟件,文檔注釋應該都足夠清晰,許多還會提供示例代碼。如果這些還不能讓你明白,要么是該軟件不值信賴,也就沒有重用的價值;要么是你自身的理解問題,只有求助有識之士了。”

    句號體會到:“由此可見,封裝的代碼不僅要屏蔽客戶代碼的訪問,最好還能屏蔽客戶代碼開發者的訪問。這樣既鼓勵代碼作者多寫規范文檔,又鼓勵代碼用戶多讀規范文檔。一切以規范為中心,而不以源碼實現為中心。”

    “非常好的建議!”冒號豎起拇指,“訪問控制只是個玻璃罩,能防止亂動的雙手,卻防止不了偷窺的雙眼。它至多只能維護語法上的封裝和信息隱藏,而語義上的封裝只有靠規范來維護。對程序員而言,前者是一種需要學習的知識,后者是一種需要培養的素質。”

    嘆號覺得腦子里仍是半清半濁:“能舉個語義上違反封裝的例子嗎?”

    冒號爽快地接受請求:“第一個例子是上節課談到對象封裝時作為反例的User類,其中getBirthday直接返回了內部域birthday的引用。如果你在調用getBirthday后對返回值進行修改,就是一種違反封裝的行為。”

    嘆號有些愕然:“那不是User類本身首先違反封裝原則的嗎?”

    冒號食指微揚:“不錯,User類的作者錯在授人以隙,而你錯在乘人之隙。”

    眾人一陣哄笑,嘆號面紅耳赤,仿佛真的犯了錯似的。

    “剛才我們說過,超市開放貨艙后門屬管理不善,而客戶鉆進去取貨屬不守規矩。類似地,行人橫穿馬路的問題也有兩方面的因素。”冒號重提前例,“說回User類,其設計者肯定不希望客戶通過此種方式來修改birthday,否則也不會提供setBirthday的接口。”

    逗號頗為不服:“可是setBirthday中除了簡單的賦值什么也沒干哪!”

    “哈哈,又忍不住偷看源代碼了吧!”冒號逮了個正著,“你怎么能保證User類的作者哪天不心血來潮,在setBirthday中寫一些不同尋常的代碼?不要輕視任何一個接口,哪怕它暫時只有一個空語句的實現。事實上,許多空接口就是為將來的功能擴展預留的,隨時可能被充實,或者被子類覆蓋。”

    逗號心里話:得,又掉溝里了!

    冒號續道:“第二個例子涉及Java中的Swing。一般說來,如果一個組件的可視化性質如位置、尺寸等發生改變,都需要重新布局(layout )。凡是Swing組件(component )都要調用revalidate 方法。絕大多數情況下,setTextsetFontsetIcon等方法的實現中會自動調用revalidate,但仍有少數例外。規范文檔中又語焉不詳,令人困惑。為保證不受源碼變動的影響,同時免除記憶之困,最好在一個組件所有與布局相關的變化完畢后,專門調用一次revalidate。以輕微的性能代價換來長治久安,無疑是正確的。相反,依賴源代碼而非規范文檔編程,顯然是危險的。如果說第一個例子直接破壞了封裝,有可能馬上被察覺,該例則隱蔽得多——只要在所依賴的源代碼不變,一切都正常。然而一旦有變,后果難以預料。”

    引號不免有些感慨:“一般人熟悉JDKAPI文檔多過熟悉源碼,尚且可能犯依賴源碼編程的錯誤。如果重用同一開發組的代碼,甚至是本人的代碼,對源碼非常熟悉,偏偏文檔還匱乏,這種錯誤更是在所難免。”

    “意識到這一點就是很大的進步啊。”冒號欣慰道,“再舉一例。有時在使用一個類時,你很想重用其中一個protected方法,但當前所在的客戶類既不是其子類,所在的package也不同。怎么辦?”

    句號承認:“以前的確碰到這樣的問題,第一感覺是恨那作者太小氣:為什么不干脆將其設為public與眾共享?轉念一想,大不了寫個繼承的子類,別的事不做,專門把那些protected方法轉化為public。”

    “是不是這樣?”冒號在黑板上飛快地寫下——

    class Reserved

    {

        protected void f(){/**/}

        protected int g(){/**/}

        …

    }

    class Open extends Reserved

    {

        public void f(){super.f();}

        public int g(){return super.g();}

        …

    }

    見句號點頭,冒號問:“你不覺得有何不妥嗎?”

    “很俗很暴力。”句號的自評令眾人噴飯。

    冒號分析道:“你既然那么希望調用某個protected方法,說明它一定不平凡,但為何作者遮遮掩掩、不愿公開呢?假若他的設計是合理的,那么只有一個解釋:它是為內部或子類服務的,本就不打算對外開放。你所需要的服務要么是設計者刻意回避的,要么接口另有所在,說不定還恰好調用了你所需要的方法呢!”

    一束光芒從眾人腦際劃過。

    冒號又補充道:“不輕易公開他人的protected成員還有一個理由。正因為protected的接口比public使用的范圍狹窄得多,接口變動的可能性往往也更大,客戶應該慎用。總之,道法自然,不自然的另一面通常是不正確,請注重培養這種編程嗅覺。”

    逗號使勁吸了吸鼻子。

    冒號遂作結語:“我們提倡針對接口編程programming to an interface),避免針對實現編程programming to an implementation)。以上三例則是通過接口深入實現programming through an interface to an implementation——《code complete》),本質上正是針對實現編程。以違背服務初衷的方式享受的服務,如同盛夏的豆腐——即使沒有變質,也是不能持久的。”

    posted on 2008-08-07 19:31 鄭暉 閱讀(2208) 評論(2)  編輯  收藏 所屬分類: 冒號和他的學生們

    評論

    # re: 冒號和他的學生們(連載27)——接口服務 2008-10-18 20:23 wanget

    很好的文章,看了一下午眼睛都疼了 ^_^

    問個問題,看到了你說的“除了盜版的商業軟件,都應該能聯系到原作者“,如果公司里寫那段代碼的程序員走了,而代碼寫的又不規范,最要命的假如我本人需要維護那段代碼,而代碼又有好幾萬行……這種情況下有沒有什么好的思路呢? (C程序)  回復  更多評論   

    # re: 冒號和他的學生們(連載27)——接口服務[未登錄] 2008-10-19 12:49 鄭暉

    @wanget
    幾萬行的代碼在提交時竟然沒有明確規范的說明,此一錯;公司在代碼作者離職時沒有安排與接手人進行必要的交接,此二錯;沒有留下作者的聯系方式,此三錯。這一方面說明公司的管理不規范,另一方面也說明這部分代碼并不是那么重要,否則難以想象公司如何靠軟件開發來生存。維護這樣的代碼,首先要看這段代碼在整個系統或子系統中所扮演的角色,自己草擬一份規范說明,并與其他相關人員核實。(如果整個系統都沒有明確的規范的話,你得考慮跳槽了。)其次,找出所有這段代碼被外部調用的接口和公開變量(包括C程序中的全局變量)。最好不要依賴IDE,自己寫一段script來搜索并保留結果。這樣有兩個重要作用:一、通過客戶代碼反向推出接口的用法;二、在維護代碼時可以自由地改變其他的非接口部分,而無需擔心對其他部分代碼的影響。
      回復  更多評論   

    導航

    統計

    公告

    博客搬家:http://blog.zhenghui.org
    《冒號課堂》一書于2009年10月上市,詳情請見
    冒號課堂

    留言簿(17)

    隨筆分類(61)

    隨筆檔案(61)

    文章分類(1)

    文章檔案(1)

    最新隨筆

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 一级视频免费观看| 国产成人精品免费视频大| 久久精品亚洲一区二区| 麻花传媒剧在线mv免费观看| 亚洲国产熟亚洲女视频| 亚洲国产精品一区二区第一页免| 久操免费在线观看| 亚洲av永久无码| 亚洲情a成黄在线观看动漫尤物| 无码国产精品久久一区免费 | 久久久国产亚洲精品| 亚洲?V无码成人精品区日韩| 男的把j放进女人下面视频免费| 亚洲一卡2卡3卡4卡乱码 在线| 亚洲国产精品无码久久九九 | 亚洲日本一区二区一本一道| 57pao一国产成视频永久免费| 日本一区二区在线免费观看 | 亚洲欧洲日本天天堂在线观看| 免费又黄又爽的视频| 84pao强力永久免费高清| 精品久久久久久亚洲综合网| 日本久久久久亚洲中字幕| 亚洲成a人片在线观看久| 国产成人福利免费视频| 久久国产乱子伦精品免费午夜| 国产AV旡码专区亚洲AV苍井空| 国产日韩亚洲大尺度高清| 日韩精品免费一区二区三区| 鲁大师在线影院免费观看 | 日韩中文无码有码免费视频| 99国产精品视频免费观看| 又硬又粗又长又爽免费看| 亚洲男人天堂2018av| 亚洲高清中文字幕综合网| 中文字幕精品亚洲无线码二区 | 国产麻豆视频免费观看| 久久久久久AV无码免费网站下载| 黄色a级免费网站| 亚洲av日韩综合一区久热| 亚洲日本香蕉视频|