哈工大软件构造课程知识点总结(三)
系列文章目錄
哈工大軟件構(gòu)造課程知識點總結(jié)(一)
哈工大軟件構(gòu)造課程知識點總結(jié)(二)
哈工大軟件構(gòu)造課程知識點總結(jié)(三)
哈工大軟件構(gòu)造課程知識點總結(jié)(四)
哈工大軟件構(gòu)造課程知識點總結(jié)(五)
哈工大軟件構(gòu)造課程知識點總結(jié)(六)
文章目錄
- 系列文章目錄
- 簡介
- Chapter 5:Designing Specification
- 規(guī)約簡要介紹
- 行為等價性
- 規(guī)約的設(shè)計
- 規(guī)約的比較
- 規(guī)約畫圖
- 如何設(shè)計好的規(guī)約
- Chapter 6:Abstract Data Type
- 抽象數(shù)據(jù)類型相關(guān)概念
- 設(shè)計抽象數(shù)據(jù)類型
- 測試抽象數(shù)據(jù)類型
- 表示獨立性
- 表示不變性
- 抽象函數(shù)
- 表示不變性(RI)與抽象函數(shù)(AF)之間的關(guān)系
- 表示泄露
- 有益的可變性
簡介
此文章是2021春哈工大軟件構(gòu)造課程Chapter 5、Chapter 6的知識點總結(jié)。
Chapter 5:Designing Specification
規(guī)約簡要介紹
“方法”是程序的積木,可以被獨立開發(fā)、測試、復(fù)用。使用“方法”的客戶端,無需了解內(nèi)部的具體實現(xiàn),這就是“抽象” 的思想。
一個完整的方法應(yīng)包含規(guī)約和實現(xiàn)兩大部分,如下圖示例:
代碼本身就蘊含著“設(shè)計決策”(如使用final關(guān)鍵字說明此變量不可變),但這遠(yuǎn)遠(yuǎn)不夠。我們需要注釋形式的“設(shè)計決策”(規(guī)約)以供自己和他人閱讀。
規(guī)約(spec)給程序員和用戶雙方都確定了責(zé)任,調(diào)用時雙方都要遵守。
規(guī)約的作用:
- 隔離“變化”,無需通知客戶端——“防火墻”
- 解耦,客戶端不需了解具體實現(xiàn)
- 提高代碼效率
規(guī)約的內(nèi)容:
- 輸入/輸出的數(shù)據(jù)類型
- 方法的功能和正確性
- 性能
只講“能做什么”,不講“如何實現(xiàn)”!
行為等價性
根據(jù)代碼的規(guī)約,站在客戶端視角看行為等價性。
例: 有以下兩個方法:
對于以下規(guī)約:
由于兩個函數(shù)都符合此規(guī)約,故此情況下它們等價。
規(guī)約的設(shè)計
前置條件(precondition):對客戶端的約束,客戶端使用方法時必須滿足的條件,使用關(guān)鍵詞requires表明。
后置條件(postcondition):對開發(fā)者的約束,方法結(jié)束時必須滿足的條件,使用關(guān)鍵詞effects表明。
- 靜態(tài)類型聲明是一種規(guī)約,可據(jù)此進(jìn)行靜態(tài)類型檢查(static checking)
- 方法前的注釋也是一種規(guī)約,但需人工判定其是否滿足
契約:如果前置條件滿足了,后置條件必須滿足;前置條件不滿足,則方法可做任何事情(最好還是處理一下,通過failing fast讓客戶端發(fā)現(xiàn)這一問題)。
規(guī)約具體設(shè)計規(guī)則:
- 參數(shù)使用@param描述,結(jié)果使用@return、@throws描述
- 如果可能,將前置條件寫入@param中,后置條件寫入@return和@throws中
- 除非在后置條件中聲明過,否則方法內(nèi)部不應(yīng)該改變輸入?yún)?shù)
- 盡量不設(shè)計修改輸入?yún)?shù)的規(guī)約,減少使用可變對象
- 描述的功能要單一、簡單、易理解
- 如果規(guī)約中需要提到“值”,只能使用抽象空間中的“值”(關(guān)聯(lián)Chapter 6)
一個具體的規(guī)約:
補充Chapter 2 黑盒測試部分:
測試用例不能依賴于具體實現(xiàn),而必須同客戶端一樣,遵守規(guī)約
出處:Chapter 5 課件 P46
規(guī)約的比較
可從規(guī)約的確定性、陳述性及強度入手進(jìn)行比較。
假如規(guī)約強度S2 >= S1,則有:
- 前置條件S2比S1更弱或相同
- 后置條件S2比S1更強或相同
較強的規(guī)約具有更放松的前置條件 + 更嚴(yán)格的后置條件
例:
(1)以下三個規(guī)約依次增強:
(2)以下兩個規(guī)約無法比較強度
相較于第一個規(guī)約,第二個的前置條件更弱了;但在滿足第一個規(guī)約的前置條件的情況下,第二個規(guī)約相較于第一個其后置條件也弱化了(沒有返回最低索引值)。
當(dāng)規(guī)約被增強時:
- 可滿足規(guī)約的實現(xiàn)方式更少
- 更多的用戶端可以使用
- 實現(xiàn)者(開發(fā)者)的自由度更小,責(zé)任更重
- 客戶端(使用者)責(zé)任更輕
規(guī)約畫圖
以find為例:
可以得到以下結(jié)論:
- 某個具體實現(xiàn),若滿足規(guī)約,則落在其范圍內(nèi),否則,在其之外
- 程序員可以在規(guī)約的范圍內(nèi)自由選擇實現(xiàn)方式,客戶端無需了解具體使用了哪個實現(xiàn)
- 規(guī)約越強,對應(yīng)的區(qū)域越小
如何設(shè)計好的規(guī)約
- 規(guī)約不應(yīng)太弱,也不能太強(權(quán)衡用戶使用與實現(xiàn)難度)
- 在規(guī)約里使用抽象類型,可以給方法的實現(xiàn)體與客戶端更大的自由度
- 是否使用前置條件取決于check的代價和方法的使用范圍
Chapter 6:Abstract Data Type
抽象數(shù)據(jù)類型相關(guān)概念
抽象數(shù)據(jù)類型(ADT)強調(diào)“作用于數(shù)據(jù)上的操作”,程序員和客戶端無需關(guān)心數(shù)據(jù)如何具體存儲的,只需設(shè)計/使用操作即可。
抽象數(shù)據(jù)類型的特性:
- 可能發(fā)生表示泄露
- 抽象函數(shù)(abstraction function) [AF]
- 表示獨立性(representation independence) [RI]
- 表示不變性(representation invariant)
抽象數(shù)據(jù)類型的操作分類:
- 構(gòu)造器(creator):t* → T,可能實現(xiàn)為構(gòu)造函數(shù)或靜態(tài)函數(shù)(工廠方法)
- 生產(chǎn)器(producer):T+, t* → T
- 觀察器(observer):T+, t* → t
- 變值器(mutator):T+, t* → void | t | T,通常返回void,也可返回非空(如本身、修改結(jié)果等)
注:T代表抽象類型自身,t是其他類型,+表示類型出現(xiàn)一次或多次,*表示類型出現(xiàn)零次或多次。
例:
- Integer.valueOf() – Creator
- new ArrayList() – Creator
- Arrays.asList() – Creator
- String.concat() – Producer
- BigInteger.mod() – Producer
- String.toUpperCase() – Producer
- List.size() – Observer
- String.length() – Observer
- Map.keySet() – Observer
- List.addAll() – Mutator
- BufferedReader.readline() – Mutator
設(shè)計抽象數(shù)據(jù)類型
規(guī)則:
注:AF、RI應(yīng)在代碼中以注釋形式寫出,而不能在Javadoc文檔中,防止被外部看到而破壞表示獨立性/信息隱藏。
后兩條規(guī)則具體示例:
Chapter 6 課件 P82 ~ P84
測試抽象數(shù)據(jù)類型
- 測試creators, producers, and mutators:調(diào)用observers來觀察結(jié)果是否滿足規(guī)約
- 測試observers:調(diào)用creators, producers, and mutators等方法產(chǎn)生或改變對象,來看結(jié)果是否正確
風(fēng)險:如果被依賴的其他方法有錯誤,可能導(dǎo)致被測試方法的測試結(jié)果失效!
表示獨立性
client使用ADT時無需考慮其內(nèi)部如何實現(xiàn),ADT內(nèi)部表示的變化不應(yīng)影響外部規(guī)約和客戶端。
違反表示獨立性的一個示例:
違反原因:ADT修改后客戶端代碼受影響(無法再使用get方法)
保持表示獨立性的一個示例:
表示不變性
表示不變性(represetation invariant, RI)可以看作:
- 某個具體的“表示”是否是“合法的”
- 所有表示值的一個子集,包含了所有合法的表示值
- 一個條件,描述了什么是“合法”的表示值
精確記錄RI——rep中所有fields何為有效
使用checkrep()私有方法檢查RI:
- 在所有可能改變表示的方法內(nèi)都要檢查
- Observer方法不改變表示,但以防萬一建議也要檢查
如何建立表示不變性:
- 構(gòu)造器和生產(chǎn)器在創(chuàng)建對象時要確保不變量為true
- 變值器和觀察器執(zhí)行時必須保持不變性
- 每個方法返回前,用checkRep()檢查不變量是否保持
用ADT不變量可取代復(fù)雜的前置條件,相當(dāng)于將復(fù)雜的前置條件封裝到了ADT內(nèi)部。
抽象函數(shù)
首先引入表示空間與抽象空間的概念:
抽象空間(A空間):客戶端看到和使用的值
表示空間(R空間):ADT對于數(shù)據(jù)的內(nèi)部表示
ADT開發(fā)者要同時關(guān)注抽象空間和表示空間,客戶端只需關(guān)注抽象空間。
表示空間 → 抽象空間的映射關(guān)系:
- 抽象空間的每個值一定有表示空間的值與其對應(yīng)——滿射
- 一些抽象空間的值可能有多個表示空間的值與之對應(yīng)——未必單射
- 表示空間中某些值可能沒有對應(yīng)的抽象空間的值——未必雙射
抽象函數(shù)(abstraction function, AF):表示空間和抽象空間之間映射關(guān)系的函數(shù),即如何去解釋表示空間中的每一個值為抽象空間中的每一個值。
精準(zhǔn)記錄AF——如何解釋每一個表示空間的值(映射關(guān)系)
表示不變性(RI)與抽象函數(shù)(AF)之間的關(guān)系
- 不同的內(nèi)部表示,需要設(shè)計不同的AF和RI
- 選擇某種特定的表示方式R,進(jìn)而指定某個子集是“合法”的(RI),并為該子集中的每個值做出“解釋”(AF)——即如何映射到抽象空間中的值
- 即使是相同的R、RI,也可能有不同的AF,即“解釋不同”
例:
選擇字符串作為字符集合的表示方式,一種可能的對應(yīng)RI、AF如下:
則有:
- “ad”、“eeee”、"abcd"滿足RI,“adad”、"abc"不滿足RI
- AF(“acfg”) = {a, b, c, f, g}
- AF(“tv”) = AF(“ttuv”) = AF(“ttuuvv”)
表示泄露
“表示泄露”不僅影響表示不變性,也影響了表示獨立性。
一旦發(fā)生,ADT內(nèi)部表示可能在程序任何位置發(fā)生改變(而不是限制在ADT內(nèi)部)。
除非迫不得已,否則不要把希望寄托于客戶端上,ADT有責(zé)任保證自己的不變性,并避免“表示泄露”。
最好的辦法就是使用immutable的類型,徹底避免表示泄露!
表示泄露的安全聲明——給出理由,證明代碼并未對外泄露其內(nèi)部表示。
有益的可變性
對于不可變的抽象數(shù)據(jù)類型,它在抽象空間內(nèi)的抽象值應(yīng)是不變的,但其內(nèi)部表示的表示空間取值是可以變化的。
例:一個使用兩個int型變量來表示分?jǐn)?shù)(AF、RI略,表示的分?jǐn)?shù)為numerator / denominator)的ADT,其toString()方法可以修改其內(nèi)部表示(化簡分?jǐn)?shù)):
/*** @return 分?jǐn)?shù)的可讀字符串表示*/ @override public String toString() {int g = gcd(numerator, denominator);/* 化簡分?jǐn)?shù),以便輸出人更易讀的值 */numerator /= g;denominator /= g;if (denominator < 0) {numerator = -numerator;denominator = -denominator;}checkRep();return (denominator > 1) ? (numerator + "/" + denominator): (numerator + ""); }注意: 這種mutation僅改變了表示空間的值,并未改變抽象空間的值,對于客戶端來說是利用了”AF未必單射“,從一個表示空間值變成了另一個表示空間值,但這并不代表不可變的類中可以隨意出現(xiàn)mutator方法!
總結(jié)
以上是生活随笔為你收集整理的哈工大软件构造课程知识点总结(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: threejs创建3d交互地图
- 下一篇: 我的世界服务器修复地图指令,我的世界怎样