1.編程語言中的功能/方法
2.規約:便於交流的編程,爲何須要規約
行爲等同規約結構:前提條件和後條件測試和驗證規約
3.設計規約分類規約圖表規約質量規約
4.總結程序員
方法:構建模塊
大型項目由小型方法構建
方法能夠單獨開發,測試和重複使用
方法的用戶不須要知道它是如何工做的 - 這被稱爲「抽象」算法
注意:調用方法時參數類型不匹配 - 靜態檢查
返回值類型是否匹配,也在靜態類型檢查階段完成編程
(1)編程中的文檔安全
Java API文檔:一個例子
類層次結構和實現的接口列表。
直接子類,併爲接口實現類。
類的描述
構建思想
方法摘要列出了咱們能夠調用的全部方法
每一個方法和構造函數的詳細描述數據結構
記錄假設編程語言
向下寫入變量的類型記錄了一個關於它的假設:例如,此變量將始終引用一個整數。函數
聲明一個變量final也是一種形式的文檔,聲明該變量在初始賦值後永遠不會改變。工具
如何函數/方法的假設?性能
便於交流的編程測試
爲何咱們須要寫下咱們的假設?
程序必須記住兩個目標:
黑客與工程
黑客每每以肆無忌憚的樂觀爲標誌
但軟件工程不是黑客行爲。 工程師是悲觀主義者:
靜態檢查有助於此
(2)規約和契約(方法)
規約(或稱爲契約)
規約是團隊合做的關鍵。 沒有規約就不可能委託實施方法的責任。
規約做爲一種契約:實施者負責知足契約,而使用該方法的客戶能夠依賴契約。
規約對雙方都有要求:當規約有先決條件時,客戶也有責任。
爲何規約?
現實:
優勢:
規約對於方法的實現者來講是很好的,由於他們給了實現者自由地改變實現而不告訴客戶。
規約也可使碼代碼更快。
契約充當客戶和實施者之間的防火牆。
對象與其用戶之間的協議
該方法作了什麼,而不是如何作
(3)行爲等價性
要肯定行爲等同性,問題是咱們是否能夠用另外一個實現替代另外一個實現
等價的概念在客戶眼中。
爲了使一個實現替代另外一個實現成爲可能,而且知道什麼時候能夠接受,咱們須要一個規約來講明客戶端依賴於什麼
注意:規約不該該談論方法類的局部變量或方法類的私有字段。
(4)規約結構:前提條件和後置條件
一個方法的規約由幾個子句組成:
先決條件是客戶(即方法的調用者)的義務。 這是調用方法的狀態。
後置條件是該方法實施者的義務。
若是前提條件適用於調用狀態,則該方法必須遵照後置條件,方法是返回適當的值,拋出指定的異常,修改或不修改對象等等。
總體結構是一個合乎邏輯的含義:若是在調用方法時前提條件成立,則在方法完成時必須保持後置條件。
若是在調用方法時前提條件不成立,則實現不受後置條件的限制。
Java中的規約
Java的靜態類型聲明其實是方法的前提條件和後置條件的一部分,該方法是編譯器自動檢查和執行的一部分。
靜態檢查
契約的其他部分必須在該方法以前的評論中進行描述,而且一般取決於人類對其進行檢查並予以保證。
參數由@param子句描述,結果由@return和@throws子句描述。
將前提條件放在@param中,並將後置條件放入@return和@throws。
可變方法的規約
若是效應沒有明確說明輸入能夠被突變,那麼咱們假設輸入的突變是隱式地被禁止的。
幾乎全部的程序員都會承擔一樣的事情。 驚喜突變致使可怕的錯誤。
慣例:
可變對象可使簡單的規約/合約很是複雜
可變對象下降了可變性
可變對象使簡單的合約變得複雜
對同一個可變對象(對象的別名)的屢次引用可能意味着程序中的多個地方 - 可能至關分散 - 依靠該對象保持一致。
按照規約說明,契約不能再在一個地方執行,例如, 一個類的客戶和一個類的實施者之間。
涉及可變對象的契約如今取決於每一個引用可變對象的每一個人的良好行爲。
做爲這種非本地契約現象的一個症狀,考慮Java集合類,這些類一般記錄在客戶端和實現者之間的很是明確的契約中。
對這樣的全局屬性進行推理的須要使得理解難度更大,而且對可變數據結構的程序的正確性有信心。
咱們仍然必須這樣作 - 爲了性能和便利性 - 可是爲了這樣作,咱們在bug安全方面付出了巨大的代價。
可變對象下降了可變性
可變對象使得客戶端和實現者之間的契約更加複雜,而且減小了客戶端和實現者改變的自由。
換句話說,使用容許更改的對象會使代碼難以改變。
(5)*測試和驗證規約
正式契約規約
Java建模語言(JML)
這是一個有優點的理論方法
缺點
文本說明 - Javadoc
實用方法
記錄每一個參數,返回值,每一個異常(選中和未選中),該方法執行的操做,包括目的,反作用,任何線程安全問題,任何性能問題。
不要記錄實施細節
語義正確性遵照契約
編譯器確保類型正確(靜態類型檢查)
靜態分析工具(如FindBugs)能夠識別許多常見問題(錯誤模式)
可是,如何確保語義的正確性?
正式驗證
使用數學方法證實正式規約的正確性
正式證實一個實現的全部可能的執行符合規約
手動努力; 部分自動化; 不能自動肯定
測試
使用受控環境中的選定輸入執行程序
目標
黑盒測試:以獨立於實現的方式檢查測試的程序是否遵循指定的規約。
(1)按規約分類
比較規約
它是如何肯定性的。 該規約是否僅爲給定輸入定義了單個可能的輸出,或容許實現者從一組合法輸出中進行選擇?
它是如何聲明的。 規約是否只是表徵輸出的結果,仍是明確說明如何計算輸出?
它有多強大。 規約是否只有一小部分法律實施或一大套?
「什麼使一些規約比其餘規約更好?」
如何比較兩種規約的行爲來決定用新規約替換舊規約是否安全?
規約S2強於或等於規約S1若是
那麼知足S2的實現也能夠用來知足S1,在程序中用S2代替S1是安全的。
規則:
若是S3既不強於也不弱於S1,則規約可能會重疊(所以存在僅知足S1,僅S3,以及S1和S3的實現)或者可能不相交。
在這兩種狀況下,S1和S3都是沒法比較的。
(2)圖表規約
這個空間中的每一個點表明一個方法實現。
規約在全部可能的實現的空間中定義了一個區域。
一個給定的實現要麼按照規約行事,要知足前置條件 - 隱含 - 後置契約(它在區域內),或者不(在區域外)。
實現者能夠自由地在規約中移動,更改代碼而不用擔憂會破壞客戶端。
這對於實現者可以提升其算法的性能,代碼的清晰度或者在發現錯誤時改變他們的方法等而言是相當重要的。
客戶不知道他們會獲得哪些實現。
當S2比S1強時,它在此圖中定義了一個較小的區域。
較弱的規約定義了一個更大的區域。
強化實施者的後置條件意味着他們自由度較低,對產出的要求更強。
弱化前提意味着:實現必須處理先前被規約排除的新輸入。
(3)設計好的規約
規約的質量
什麼是一個好方法? 設計方法意味着主要編寫一個規約。
關於規約的形式:它顯然應該簡潔,清晰,結構良好,以便閱讀。
然而,規約的內容很難規定。 沒有一個可靠的規則,但有一些有用的指導方針。
規約應該是連貫的(內聚的)
該規約不該該有不少不一樣的狀況。 冗長的參數列表,深層嵌套的if語句和布爾型標誌都是麻煩的跡象。
除了可怕地使用全局變量和打印而不是返回以外,規約不是一致的 - 它執行兩個不一樣的事情,計算單詞並找出最長的單詞。
調用的結果應該是信息豐富的
若是返回null,則沒法肯定密鑰是否先前未綁定,或者其實是否綁定爲null。這不是一個很好的設計,由於返回值是無用的,除非您肯定沒有插入null。
規約應該足夠強大
規約應給予客戶在通常狀況下足夠強大的保證 - 它須要知足其基本要求。 - 在規定特殊狀況時,咱們必須格外當心,確保它們不會破壞原本是有用的方法。例如,對於一個不合理的論證拋出異常,但容許任意的突變是沒有意義的,由於客戶端將沒法肯定實際發生了什麼樣的突變。
規約也應該足夠薄弱
這是一個很差的規約。
規約應該使用抽象類型
用抽象類型編寫咱們的規約爲客戶和實現者提供了更多的自由。
在Java中,這一般意味着使用接口類型,如Map或Reader,而不是像HashMap或FileReader這樣的特定實現類型。
這強制客戶端傳入一個ArrayList,並強制實現返回一個ArrayList,即便可能存在他們但願使用的替代List實現。
先決條件仍是後置條件?
是否使用前提條件,若是是,則在繼續以前,方法代碼是否應該嘗試確保先決條件已知足?
對於程序員:
若是檢查一個條件會使方法變得難以接受,那麼一般須要一個先決條件。
對用戶而言:
因此方法的用戶不喜歡先決條件。
關鍵因素是檢查的費用(編寫和執行代碼)以及方法的範圍。
若是隻在類本地調用,則能夠經過仔細檢查調用該方法的全部類來解決前提條件。
若是該方法是公開的,而且被其餘開發人員使用,那麼使用前提條件將不太明智。 相反,像Java API類同樣,您應該拋出一個異常。
總結
規約做爲程序實現者與其客戶之間的關鍵防火牆。
它使得單獨的開發成爲可能:客戶端能夠自由地編寫使用該過程的代碼,而無需查看其源代碼,而且實現者能夠自由地編寫實現該過程的代碼而不知道它將如何使用。
減小錯誤保證安全
準備好改變
聲明性規約在實踐中是最有用的。
先決條件(削弱了規約)使客戶的生活更加艱難,但明智地應用它們是軟件設計師的重要工具,容許實施者作出必要的假設。
減小錯誤保證安全
容易理解
準備好改變