一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

什么是契約——Eiffel的觀點(diǎn)

 條山石頭 2015-06-19

什么是契約——Eiffel的觀點(diǎn)

    假設(shè)你現(xiàn)在正在面試,主考不緊不慢地給出下一道題目:“請用C語言寫一個(gè)類似strcpy的函數(shù)。要考慮可能發(fā)生的異常情況。” 你會(huì)怎么做呢?很明顯,對方不是在考察你的編程能力,因?yàn)閺?fù)制字符串實(shí)在太容易了。對方是在考察你的編程風(fēng)格(習(xí)慣),或者說,要看看你編碼的質(zhì)量。

    下面是多種可能的做法:

    void
    string_copy1(char* dest, const char* source)
    {
      assert(dest != NULL); /* 使用斷言 */
      assert(source != NULL);
     
      while (*source != '/0') {
        *dest = *source;
        ++dest;
        ++source;
      }

      *dest = '/0';
    }

    void
    string_copy2(char* dest, const char* source)
    {
      if (dest != NULL && source != NULL) {  /* 對錯(cuò)誤消極靜默 */
        while (*source != '/0') {
          *dest = *source;
          ++dest;
          ++source;
        }

        *dest = '/0';
      }
    }

    int
    string_copy3(char* dest, const char* source)
    {
      if (dest != NULL && source != NULL) {
        while (*source != '/0') {
          *dest = *source;
          ++dest;
          ++source;
        }

        *dest = '/0';
        return SUCCESS;  /* 返回表示正確的值 */
      }                         
      else {
       errno = E_INVALIDARG;  /* 設(shè)定錯(cuò)誤號 */
       return FAILED;         /*  返回表示錯(cuò)誤的值 */
      }
    }
   
    // C++
    void
    string_copy4(char* dest, const char* source)
    {
       if (dest == NULL || source == NULL)
         throw Invalid_Argument_Error();  /*  拋出異常 */

       while (*source != '/0') {
         *dest = *source;
         ++dest;
         ++source;
       }

       *dest = '/0';
    }

    如果你是主考,不知道面對這樣四份答卷,你的評分如何?當(dāng)然,你可以心里揣著一個(gè)“標(biāo)準(zhǔn)答案”,“順我者昌,逆我者亡”。但是如果以認(rèn)真的態(tài)度面對這四份答卷,我想很多人都會(huì)難以抉擇。

    因?yàn)檫@里涉及到了軟件開發(fā)中的一個(gè)帶有本質(zhì)性的難題——錯(cuò)誤處理。

    歷來錯(cuò)誤處理一直是軟件開發(fā)者所面臨的最大困難之一。Bjarne Stroustrup在談到其原因時(shí)說道,能夠探察錯(cuò)誤的一方不知道如何處理錯(cuò)誤,知道如何處理錯(cuò)誤的一方?jīng)]有能力探察錯(cuò)誤,而直接采用防御性代碼來解決,會(huì)使得程序的正常結(jié)構(gòu)被打亂,從而帶來更多的錯(cuò)誤。這種困境是非常難以應(yīng)對的——費(fèi)心耗力而未必有回報(bào)。因此,更多的人采用鴕鳥戰(zhàn)術(shù),對可能發(fā)生的錯(cuò)誤視而不見,任其自然。

    C++、Java和其他語言對錯(cuò)誤處理問題的回答是異常機(jī)制。這種機(jī)制在正常的程序執(zhí)行流之外開辟了專門的信道,專門用來在不同程序模塊之間報(bào)告錯(cuò)誤,解決上述錯(cuò)誤探察與處理策略分散的矛盾。然而,有了異常處理機(jī)制后,開發(fā)者開始有一種傾向,就是使用異常來處理所有的錯(cuò)誤。我曾經(jīng)就這個(gè)問題在comp.lang.c++.moderated上展開討論,結(jié)果是發(fā)現(xiàn)有相當(dāng)多的人,包括Boost開發(fā)組里的很多專家,都認(rèn)為異常是錯(cuò)誤處理的通用解決方案。

    對此我不能贊同。并且我認(rèn)為濫用異常比不用異常的危害更大。

    The Pragmatic Programmer是一本在國外程序員中間頗為流行的書,其中在講到錯(cuò)誤處理時(shí),有一句箴言:
   
    “只在真正異常的狀況下使用異常。”

    書中舉了一個(gè)例子,如果你需要當(dāng)前目錄下的一個(gè)名叫“app.dat”的文件,而這個(gè)文件不存在,那么這不叫異常狀況,這是你應(yīng)該預(yù)料到的、并且顯式處理的情況。而如果你要到Windows目錄下尋找user.dat文件,卻沒找到,那才叫做異常狀況——因?yàn)槊恳粋€(gè)正常運(yùn)行的Windows系統(tǒng)都應(yīng)該有這個(gè)文件。

    我非常贊成書中的那句忠告,可是究竟什么是“真正異?!钡臓顩r?書中的這個(gè)例子顯然只是一個(gè)頗具感性的、寓言似的故事,具有所有寓言的共同特點(diǎn)——讀起來覺得豁然開朗,收獲很大,實(shí)際上幫不了你什么忙。這種例子對于我們的實(shí)際開發(fā),仍然提供不了真正的幫助。

    究竟應(yīng)該如何看待錯(cuò)誤?怎樣才能最好地錯(cuò)誤處理?

    說實(shí)話,在這兩個(gè)問題上,我們所見到的大部分語言都沒有給出很好的回答。C秉承一貫風(fēng)格,把所有的東西推給開發(fā)者考慮;Ada發(fā)明了異常,但是又為異常所累(知道阿里亞納5火箭的處女航為什么失敗嗎?);C++企圖將Ada的異常機(jī)制融合進(jìn)自己的體系中,結(jié)果異常成了C++中最難以處理的東西;Java和C#顯然都沒有耐心重新考慮錯(cuò)誤處理這樁事,而只是簡單的將C++的異常機(jī)制完善化了事。

    與上述這些語言不同,Eiffel從一開始就把錯(cuò)誤處理放在核心的位置上予以考慮,并以“契約”思想為核心,建立了整個(gè)的錯(cuò)誤處理思想體系。在我了解的語言里,Eiffel是對這個(gè)問題思考最為深刻一個(gè),因此,Eiffel歷來享有“高質(zhì)量系統(tǒng)開發(fā)語言”的聲譽(yù)。(事實(shí)上,Bertrand Meyer很不喜歡別人稱Eiffel為“編程語言”,他反復(fù)強(qiáng)調(diào),Eiffel是一個(gè)Software Development Framework。不過本文只涉及語言特性,所以姑且稱Eiffel語言。)

    Eiffel把軟件錯(cuò)誤產(chǎn)生的本質(zhì)歸結(jié)與“契約”的破壞。Eiffel認(rèn)為,一個(gè)軟件能夠正常運(yùn)作,正確完成任務(wù),是需要一系列條件的。這些條件包括客觀運(yùn)行環(huán)境良好,操作者操作正確,軟件內(nèi)部功能正確等等。因此,軟件的正確運(yùn)行,是環(huán)境、操作者與軟件本身三方面合作的結(jié)果。相應(yīng)的,系統(tǒng)的錯(cuò)誤,也是由于三者中有一方?jīng)]有正確履行自己的職責(zé)而導(dǎo)致的。細(xì)化到軟件內(nèi)部,每個(gè)軟件都是由若干不同的模塊組成的,軟件的錯(cuò)誤,是由于某些模塊沒有正確履行自己的職責(zé)。要徹底杜絕軟件錯(cuò)誤,只有分清各自模塊的責(zé)任,并且建立機(jī)制,敦促各模塊正確履行自己的責(zé)任,然后才有可能做到Bug-free。(鑒于系統(tǒng)中錯(cuò)綜復(fù)雜的關(guān)系,以及開發(fā)者認(rèn)識能力的局限,我認(rèn)為真正無錯(cuò)誤的系統(tǒng)是不可能的。但是當(dāng)前一般軟件系統(tǒng)中的質(zhì)量問題遠(yuǎn)遠(yuǎn)比應(yīng)有的嚴(yán)重。)

    如何保證各方恪守職責(zé)呢?Eiffel引入了契約(Contract)這個(gè)概念。這里的契約與我們通常所說的商業(yè)契約很相似,有以下幾個(gè)特點(diǎn):

1. 契約關(guān)系的雙方是平等的,對整個(gè)bussiness的順利進(jìn)行負(fù)有共同責(zé)任,沒有哪一方可以只享有權(quán)利而不承擔(dān)義務(wù)。
2. 契約關(guān)系經(jīng)常是相互的,權(quán)利和義務(wù)之間往往是互相捆綁在一起的;
3. 執(zhí)行契約的義務(wù)在我,而核查契約的權(quán)力在人;
4. 我的義務(wù)保障的是你的利益,而你的義務(wù)保障的是我的利益;
 
    將契約關(guān)系引入到軟件開發(fā)領(lǐng)域,尤其是面向?qū)ο箢I(lǐng)域之后,在觀念上給我們帶來了幾大沖擊:

1. 一般的觀點(diǎn),在軟件體系中,程序庫和組件庫被類比為server,而使用程序庫、組件庫的程序被視為client。根據(jù)這種C/S關(guān)系,我們往往對庫程序和組件的質(zhì)量提出很嚴(yán)苛的要求,強(qiáng)迫它們承擔(dān)本不應(yīng)該由它們來承擔(dān)的責(zé)任,而過分縱容client一方,甚至要求庫程序去處理明顯由于client錯(cuò)誤造成的困境??陀^上導(dǎo)致程序庫和組件庫的設(shè)計(jì)和編寫異常困難,而且質(zhì)量隱患反而更多;同時(shí)client一方代碼大多松散隨意,質(zhì)量低劣。這種情形,就好像在一個(gè)權(quán)責(zé)不清的企業(yè)里,必然會(huì)養(yǎng)一批尸位素餐的混混,苦一批任勞任怨,不計(jì)得失的老黃牛。引入契約觀念之后,這種C/S關(guān)系被打破,大家都是平等的,你需要我正確提供服務(wù),那么你必須滿足我提出的條件,否則我沒有義務(wù)“排除萬難”地保證完成任務(wù)。

2. 一般認(rèn)為在模塊中檢查錯(cuò)誤狀況并且上報(bào),是模塊本身的義務(wù)。而在契約體制下,對于契約的檢查并非義務(wù),實(shí)際上是在履行權(quán)利。一個(gè)義務(wù),一個(gè)權(quán)利,差別極大。例如上面的代碼:
    if (dest == NULL) { ... }
這就是義務(wù),其要點(diǎn)在于,一旦條件不滿足,我方(義務(wù)方)必須負(fù)責(zé)以合適手法處理這尷尬局面,或者返回錯(cuò)誤值,或者拋出異常。而:
    assert(dest != NULL);
這是檢查契約,履行權(quán)利。如果條件不滿足,那么錯(cuò)誤在對方而不在我,我可以立刻“撕毀合同”,罷工了事,無需做任何多余動(dòng)作。這無疑可以大大簡化程序庫和組件庫的開發(fā)。

3. 契約所核查的,是“為保證正確性所必須滿足的條件”,因此,當(dāng)契約被破壞時(shí),只表明一件事:軟件系統(tǒng)中有bug。其意義是說,某些條件在到達(dá)我這里時(shí),必須已經(jīng)確保為“真”。誰來確保?應(yīng)該是系統(tǒng)中的其他模塊在先期確保。如果在我這里發(fā)現(xiàn)契約沒有被遵守,那么表明系統(tǒng)中其他模塊沒有正確履行自己的義務(wù)。就拿上面提到的“打開文件”的例子來說,如果有一個(gè)模塊需要一個(gè)FILE*,而在契約檢查中發(fā)現(xiàn)該指針為NULL,則意味著有一個(gè)模塊沒有履行其義務(wù),即“檢查文件是否存在,確保文件以正確模式打開,并且保證指針的正確性”。因此,當(dāng)契約檢查失敗時(shí),我們首先要知道這意味著程序員錯(cuò)誤,而且要做的不是糾正契約核查方,而是糾正契約提供方。換句話說,當(dāng)你發(fā)現(xiàn):
     assert(dest != NULL);
報(bào)錯(cuò)時(shí),你要做的不是去修改你的string_copy函數(shù),而是要讓任何代碼在調(diào)用string_copy時(shí)確保dest指針不為空。


4. 我們以往對待“過程”或“函數(shù)”的理解是:完成某個(gè)計(jì)算任務(wù)的過程,這一看法只強(qiáng)調(diào)了其目標(biāo),沒有強(qiáng)調(diào)其條件。在這種理解下,我們對于exception的理解非常模糊和寬泛:只要是無法完成這個(gè)計(jì)算過程,均可被視為異常,也不管是我自己的原因,還是其他人的原因(典型的權(quán)責(zé)不清)。正是因?yàn)檫@種模糊和寬泛,“究竟什么時(shí)候應(yīng)該拋出異常”成為沒有人能回答的問題。而引入契約之后,“過程”和“函數(shù)”被定義為:完成契約的過程?;谄跫s的相互性,如果這個(gè)契約的失敗是因?yàn)槠渌K未能履行契約,本過程只需報(bào)告,無需以任何其他方式做出反應(yīng)。而真正的異常狀況是“對方完全滿足了契約,而我依然未能如約完成任務(wù)”的情形。這樣以來,我們就給“異?!毕铝艘粋€(gè)清晰、可行的定義。

5. 一般來說,在面向?qū)ο蠹夹g(shù)中,我們認(rèn)為“接口”是唯一重要的東西,接口定義了組件,接口確定了系統(tǒng),接口是面向?qū)ο笾形覀兾ㄒ恍枰P(guān)心的東西,接口不僅是必要的,而且是充分的。然而,契約觀念提醒我們,僅僅有接口還不充分,僅僅通過接口還不足以傳達(dá)足夠的信息,為了正確使用接口,必須考慮契約。只有考慮契約,才可能實(shí)現(xiàn)面向?qū)ο蟮哪繕?biāo):可靠性、可擴(kuò)展性和可復(fù)用性。反過來,“沒有契約的復(fù)用根本就是瞎胡鬧。(Bertrand Meyer語)”。

    由上述觀點(diǎn)可以看出,雖然Eiffel所倡導(dǎo)的Design By Contract在表象上不過是系統(tǒng)化的斷言(assertion)機(jī)制,然而在背后,確實(shí)是完全的思想革新。正如Ivar Jacoboson訪華時(shí)對《程序員》雜志所說:“我認(rèn)為Bertrand Meyer的方向——Design by Contract——是正確的方向,我們都會(huì)沿著他的足跡前進(jìn)。我相信,大型廠商(微軟、IBM,當(dāng)然還有Rational)都不會(huì)對Bertrand Meyer的成就坐視不理。所有這些廠商都會(huì)在這個(gè)方向上有所行動(dòng)?!保▍⒁姟冻绦騿T》2002年第11期,P22)。

續(xù)篇-契約思想的一個(gè)反面案例   myan(原作)   

剛剛發(fā)表了《什么是契約》一文,突然發(fā)現(xiàn)自己通篇都在寫理論,沒有實(shí)例來證明。所以趕快補(bǔ)充一個(gè)反面案例——C++ IOStream。說是反面,不是因?yàn)镮OStream庫設(shè)計(jì)得不精彩(恰恰相反,你很難找到比IOStream設(shè)計(jì)更為精彩的C++庫了),而是想展示一下,在沒有契約概念的思想體系里,組件設(shè)計(jì)將為權(quán)責(zé)不清的錯(cuò)誤處理付出多大的代價(jià)。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品偷拍视频一区| 国产成人精品国产成人亚洲| 亚洲国产一级片在线观看| 亚洲一区二区亚洲日本| 超薄肉色丝袜脚一区二区| 好吊日在线观看免费视频| 国产女同精品一区二区| 日韩美女偷拍视频久久| 好吊日成人免费视频公开| 欧美日韩亚洲国产综合网 | 久热久热精品视频在线观看| 老富婆找帅哥按摩抠逼视频| 中文字幕一区二区久久综合| 国产自拍欧美日韩在线观看| 精品一区二区三区乱码中文| 激情图日韩精品中文字幕| 亚洲深夜精品福利一区| 久久精视频免费视频观看| 操白丝女孩在线观看免费高清| 成人欧美精品一区二区三区| 人妻一区二区三区多毛女| 国产成人午夜在线视频| 国产亚洲神马午夜福利| 日韩日韩日韩日韩在线| 欧洲自拍偷拍一区二区| 国产熟女高清一区二区| 亚洲精品一区三区三区| 日本不卡一区视频欧美| 玩弄人妻少妇一区二区桃花| 国产成人精品综合久久久看| 区一区二区三中文字幕| 欧美日韩国产自拍亚洲| 国产成人高清精品尤物| 国产高清精品福利私拍| 国产又猛又大又长又粗| 国产成人午夜福利片片| 亚洲国产精品久久综合网| 熟女高潮一区二区三区| 免费人妻精品一区二区三区久久久| 麻豆tv传媒在线观看| 日韩精品视频高清在线观看|