2022哈工大软件构造课程总结与经验分享(复习指导)
- 一.軟構1-3講
- 1.軟件構造的多維度視圖和質量目標
- 2.軟件測試與測試優先的編程
- 3.軟件構造過程與配置管理
- 二.軟構4-8講
- 4.數據類型與類型檢驗
- 5.設計規約
- 6.抽象數據類型 (ADT)
- 7.面向對象的編程
- 8.ADT和OOP中的“等價性”
- 三.軟構9-12講
- 9.面向復用的軟件構造技術
- 10.面向可維護性的構造技術
- 11.面向可復用性和可維護性的設計模式
- 12.面向正確性與健壯性的軟件構造
一.軟構1-3講
1.軟件構造的多維度視圖和質量目標
多維度視圖:
外部質量指標有:正確性:測試和調試、防御式編程、形式化方法(check\ensure\guarantee)
健壯性:針對異常情況的處理(對正確性的補充) 可擴展性:對軟件的規約進行修改,應對變化(簡約主義設計、分離主義設計)
可復用性:一次開發,多次使用
兼容性:不同軟件系統之間互相可容易集成(保持設計的同構性\標準化)
性能:更少的資源占用
可移植性:軟件可方便的在不同的技術環境之間移植
易用性、更多功能(不好)、及時性……
正確性:軟件的行為要嚴格的符合規約中定義的行為
健壯性:出現規約定義之外的情形的時候,軟件要做出恰當的反應內部質量指標有代碼可讀性可理解性清晰性等等。
二者區別在于外部質量因素影響用戶,內部質量因素影響軟件本身和它的開發者。
2.軟件測試與測試優先的編程
黑盒測試用例的設計:
黑盒測試:對程序外部表現出來的行為的測試,檢查功能,不關心內部實現細節
test case = {test inputs + execution conditions+ expected results}
測試用例:輸入+執行條件+期望結果
等價類劃分、邊界值分析:
每個等價類代表著對輸入約束加以滿足/違反的有效/無效數據的集合
相似的輸入,將會展示相似的行為。故可從每個等價類中選一個代表作為測試用例即可
邊界值分析方法是對等價類劃分方法的補充
在等價類劃分時,將邊界作為等價類之一加入考慮
3.軟件構造過程與配置管理
本地版本控制系統:倉庫存儲于開發者本地機器無法共享和協作
集中式版本控制系統:倉庫存儲于獨立的服務器,支持多開發者之間的協作
分布式版本控制系統:倉庫存儲于獨立的服務器+每個開發者的本地機器
Git 的結構、工作原理、基本指令:
二.軟構4-8講
4.數據類型與類型檢驗
基本數據類型和對象數據類型:
Mutable/Immutable:
值的改變、引用的改變:
防御式拷貝:
改變一個變量:將該變量指向另一個值的存儲空間
改變一個變量的值:將該變量當前指向的值的存儲空間中寫入一個新的值
不變數據類型:一旦被創建,其值不能改變
如果編譯器無法確定 final 變量不會改變,就提示錯誤,這也是靜態類型檢查的一部分
例如,String 是一個不可變數據類型,StringBuilder 是一個可變數據類型
使用不可變類型,對其頻繁修改會產生大量的臨時拷貝(需要垃圾回收),可變類型最少化拷貝以提高效率
如果有多個引用(別名),使用可變類型就非常不安全
畫Snapshot diagram圖:
不可變對象:用雙線橢圓
不可變的引用 final:用雙線箭頭(引用是不可變的,但指向的值卻可以是可變的)
5.設計規約
規約包括:
前置條件,對客戶端的約束,在使用方法時必須滿足的條件
后置條件,對開發者的約束,方法結束時必須滿足的條件
(如果前置條件滿足了,后置條件必須滿足)
靜態類型聲明是一種規約,可據此進行靜態類型檢查 static checking
方法前的注釋也是一種規約,但需人工判定其是否滿足
行為等價性:實現的兩個方法是否可以相互替換(對于用戶)
如果兩個函數符合相同規約,則它們等價
規約的強度:
不限定太強的 precondition,而是在 postcondition 中拋出異常:輸入不合法
如果只在類的內部使用該方法(private),那么可以不使用前置條件,在使用該方法的各個位置進行 check——責任交給內部 client
如果在其他地方使用該方法(public),那么必須要使用前置條件,若client 端不滿足則方法拋出異常
6.抽象數據類型 (ADT)
ADT 操作的四種類型:
構造器 Creaters:實現為構造函數或靜態函數
生產器 Producers(concat() method of String)
觀察器 Observers(size() method of List)
變值器 Mutators(add() method of List)改變對象屬性的方法
不可變類型沒有mutators
表示獨立性:
client 使用 ADT 時無需考慮其內部如何實現,ADT 內部表示的變化不應影響外部 spec 和客戶端
表示泄露:
不僅影響不變性,也影響了表示獨立性:無法在不影響客戶端的情況下改變其內部表示
不變量、表示不變量 RI:
不變量:在任何時候總是 true,immutability 就是一個典型的"不變量"
7.面向對象的編程
接口、抽象類、具體類:
接口(確定 ADT 的規約)和類(實現 ADT):定義和實現 ADT
接口之間可以繼承與擴展
一個類可以實現多個接口
一個接口可以有多種實現類
抽象類:包含至少一個沒有實現的抽象方法的類
接口是只有抽象方法的抽象類
嚴格繼承:子類只能添加新方法,無法重寫超類中的方法
多態:特殊多態(功能重載)、參數化多態、子類型多態\包含多態
重載:多個方法具有同樣的名字,但有不同的參數列表或返回值類型,是靜態多態,在編譯階段進行靜態類型檢查(override 在運行階段進行動態類型檢查)
泛型:
泛型接口,非泛型的實現類
泛型接口,泛型的實現類
子類型多態:不同類型的對象可以統一的處理而無需區分
8.ADT和OOP中的“等價性”
等價性 equals()和==:
等價的對象必須有相同的 hashCode
== 對基本數據類型,使用==判定相等(引用等價性)
對對象類型,使用 equals()(對象等價性)
如果用= =,是在判斷兩個對象身份標識 ID 是否相等(指向內存里的同一段空間)
在自定義 ADT 時,需要重寫 Object 的 equals()
可變對象的觀察等價性、行為等價性:
觀察等價性:在不改變狀態的情況下,兩個 mutable 對象是否看起來一致
行為等價性:調用對象的任何方法都展示出一致的結果
對可變類型來說,往往傾向于實現嚴格的觀察等價性
但在有些時候,觀察等價性可能導致 bug,甚至可能破壞 RI
在 JDK 中,不同的 mutable 類使用不同的等價性標準
對可變類型,實現行為等價性即可
也就是說,只有指向同樣內存空間的 objects,才是相等的
所以對可變類型來說,無需重寫這兩個函數,直接繼承 Object 的兩個方法即可
三.軟構9-12講
9.面向復用的軟件構造技術
面向復用編程:開發出可復用的軟件
基于復用編程:利用已有的可復用軟件搭建應用系統
白盒復用:源代碼可見,可修改和擴展
黑盒復用:源代碼不可見,不能修改
白盒框架,通過代碼層面的繼承進行框架擴展
黑盒框架,通過實現特定接口/delegation 進行框架擴展
LSP: 子類必須能替換它們的父類
子類型多態:客戶端可用統一的方式處理不同類型的對象
協變:父類型到子類型,更具體的規約,不變或更具體的返回值類型和異常類型
反協變\逆變:父類型到子類型,更具體的規約,不變或更抽象的參數類型
泛型不是協變的
委派/委托:一個對象請求另一個對象的功能
委派是復用的一種常見形式
“委托”發生在 object 層面,而“繼承”發生在 class 層面
接口的組合:
使用接口定義系統必須對外展示的不同側面的行為
接口之間通過 extends 實現行為的擴展(接口組合) 類 implements 組合接口從而規避了復雜的繼承關系
白盒框架,通過代碼層面的繼承進行框架擴展
黑盒框架,通過實現特定接口/delegation 進行框架擴展
白盒框架的原理與實現:繼承
黑盒框架的原理與實現:委派
10.面向可維護性的構造技術
可維護性的常見度量指標:
圈復雜度、代碼行數、可維護性指數、繼承的層次數、類之間的耦合度、單元測試的覆蓋度
聚合度與耦合度:
模塊化編程:高內聚、低耦合(子程序之間的相關聯性)、分離關注點、信息隱藏
評估:可分解性、可組合性、可理解性、可持續性(發生變化時受影響范圍最小)、出現異常后的保護(出現異常后受影響范圍最小)
規則:直接映射、盡可能少的接口、盡可能小的接口、顯式接口、信息隱藏
SOLID:
SRP 單一責任原則:不應該有多于 1 個原因讓你的 ADT 發生變化, 否則就拆分開
OCP:面向變化的開放/封閉原則
對擴展性的開放:模塊的行為應是可擴展的,從而該模塊可表現出新的行為以滿足需求的變化
對修改的封閉性:模塊自身的代碼是不應被修改的,擴展模塊行為的一般途徑是修改模塊的內部實現
關鍵的解決方案:抽象技術
LSP:Liskov 替換原則
ISP:接口隔離原則
不能強迫客戶端依賴于它們不需要的接口:只提供必需的接口
DIP:依賴轉置原則
抽象的模塊不應依賴于具體的模塊
具體應依賴于抽象
語法、正則表達式(運用形式語言與自動機課程知識很好理解):
輸入文件有特定格式,程序需讀取文件并從中抽取正確的內容用語法定義一個“字符串”
11.面向可復用性和可維護性的設計模式
1.創建型模式
工廠方法模式(虛擬構造器): 當 client 不知道要創建哪個具體類的實例,或者不想在 client 代碼中指明要具體創建的實例時,用工廠方法。定義一個用于創建對象的接口,讓其子類來決定實例化哪一個類,從而使一個類的實例化延遲到其子類
2.結構化模式
適配器:將某個類/接口轉換為 client 期望的其他形式,通過增加一個接口,將已存在的子類封裝起來,client 面向接口編程,從而隱藏了具體子類
裝飾器:
用每個子類實現不同的特性
對每一個特性構造子類,通過委派機制增加到對象上
3.行為類模式
策略:
有多種不同的算法來實現同一個任務,但需要 client 根據需要動態切換算法,而不是寫死在代碼里為不同的實現算法構造抽象接口,利用 delegation,運行時動態傳入 client 傾向的算法類實例
模板(Template):
做事情的步驟一樣,但具體方法不同
共性的步驟在抽象類內公共實現,差異化的步驟在各個子類中實現
使用繼承和重寫實現模板模式
迭代:
客戶端希望遍歷被放入容器/集合類的一組 ADT 對象,無需關心容器的具體類型
也就是說,不管對象被放進哪里,都應該提供同樣的遍歷方式
讓自己的集合類實現 Iterable 接口,并實現自己的獨特 Iterator 迭代器(hasNext, next, remove),允許客戶端利用這個迭代器進行顯式或隱式的迭代遍歷
訪問:
對特定類型的 object 的特定操作(visit),在運行時將二者動態綁定到一起,該操作可以靈活更改,無需更改被 visit 的類
將數據和作用于數據上的某種/些特定操作分離開來
為 ADT 預留一個將來可擴展功能的“接入點”,外部實現的功能代碼可以在不改變 ADT 本身的情況下通過 delegation 接入 ADT
12.面向正確性與健壯性的軟件構造
健壯性和正確性:
健壯性:系統在不正常輸入或不正常外部環境下仍能夠表現正常的程度
處理未期望的行為和錯誤終止即使終止執行,也要準確/無歧義的向用戶展示全面的錯誤信息
錯誤信息有助于進行 debug
正確性:程序按照 spec 加以執行的能力,是最重要的質量指標
throwable:
內部錯誤:程序員通常無能為力,一旦發生,想辦法讓程序優雅的結束(用戶輸入錯誤、設備錯誤、物理限制)
異常:你自己程序導致的問題,可以捕獲、可以處理
Error/Runtime 異常、其他異常:
運行時異常,是程序源代碼中引入的故障所造成的,如果在代碼中提前進行驗證,這些故障就可以避免
Checked 異常、Unchecked 異常:
Unchecked exceptions
不需要在編譯的時候用 try…catch 等機制處理
Checked exception
Checked 異常的處理機制:
聲明、拋出、捕獲、處理、清理現場、釋放資源等
如果子類型中 override 了父類型中的函數,那么子類型中方法拋出的異常不能比父類型拋出的異常類型更寬泛
異常發生后,如果找不到處理器,就終止執行程序,在控制臺打印出 stack trace
也可以不在本方法內處理而是傳遞給調用方,由 client 處理(“推卸責任”)
本來 catch 語句下面是用來做 exception handling 的,但也可以在catch 里拋出異常(rethrowing),目的是,更改 exception 的類型,更方便 client 端獲取錯誤信息并處理
Finally clause
當異常拋出時,方法中正常執行的代碼被終止
如果異常發生前曾申請過某些資源,那么異常發生后這些資源要被恰當的清理
不管程序是否碰到異常,finally 都會被執行
斷言的作用、應用場合:
Fail fast,避免擴散
檢查前置條件是防御式編程的一種典型形式
斷言:在開發階段的代碼中嵌入,檢驗某些“假設”是否成立。若成立,表明程序運行正常,否則表明存在錯誤
對代碼所做的假設都保持正確
用于:
內部不變量 aasert x > 0
表示不變量 checkRep()
控制流不變量 switch-case
方法的前置條件、方法的后置條件
斷言一旦 false,程序就停止執行
外部錯誤要使用 Exception 機制去處理,即使 spec 被違反,也不應 通過 assert 直接 fail,而是應拋出具體的 runtime 異常
斷言非常影響運行時的性能
使用異常來處理你“預料到可以發生”的不正常情況
使用斷言處理“絕不應該發生”的情況
總結
以上是生活随笔為你收集整理的2022哈工大软件构造课程总结与经验分享(复习指导)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jffs2制作与烧写
- 下一篇: qemu编译