2021哈工大软件构造期末考点复习笔记
第一節 多維視圖和質量目標
軟件構造多維度視圖
紅色標注為重點(考試會考選擇題)
Moment 特定時刻的軟件形態 Period 軟件形態隨時間的變化
AST (Abstract Syntax Tree) 抽象語法樹
SCI (Software Configuration Item) 配置項
concurrent multithreads 并發多線程
內部質量/外部質量
外部質量因素影響用戶,內部質量因素影響軟件本身和它的開發者
外部質量取決于內部質量
軟件的內部屬性和外部屬性(判斷)
外部質量因素
正確性(Correctness)、健壯性(Robustness)(針對異常情況處理)、可擴展性(Extendibility)、可復用性(Reusability)、兼容性(Compatibility)、性能(Efficiency)、可移植性(Portability)(Java的優點之一)、易用性(Easy of use)、功能性(Functionality)、及時性(Timeliness)
質量目標之間沖突
不同質量因素折中,但”正確性“絕不能與其他質量因素折中
第二、十二節 測試、異常、健壯性
測試(Test)
測試用例 = 輸入 + 執行條件 + 期望結果
寫spec -> 寫符合spec的測試用例 -> 寫代碼執行測試反復修改
TDD(Test-driven development)
好的測試用例的特性與好的測試的特性相似
*寫測試用例時必須既要考慮有效輸入也要考慮無效輸入
單元測試
針對軟件的最小單元模型開展測試,隔離各個模塊,容易定位錯誤和調試
Junit assertEquals assertThat查看實際值是否滿足指定條件
黑盒測試/白盒測試
黑盒測試:對程序外部表現出來的行為的測試(從spec導出測試用例,不考慮內部實現)
白盒測試:考慮內部實現細節(一般較早執行)
白盒測試一般由開發人員完成,黑盒測試一般由測試人員完成
白盒測試標準:獨立/基本路徑測試:對程序所有執行路徑進行等價類劃分,找出有代表性的最簡單的路徑(例如循環只需執行一次),設計測試用例使每一條基本路徑被至少覆蓋一次。
回歸測試
一旦程序被修改,重新執行之前的所有測試
代碼覆蓋度
函數覆蓋、語句覆蓋、分支覆蓋、條件覆蓋、路徑覆蓋
分支覆蓋和條件覆蓋:分支覆蓋 a && b – true/false 條件覆蓋:a True a False b True b False
語句覆蓋:只需要讓 a && b 語句執行一遍即可
條件覆蓋和分支覆蓋之間沒有包含關系
測試效果:路徑覆蓋 > 分支覆蓋 > 語句覆蓋(測試難度也是這個順序)
*等價類劃分(重點)
將被測函數的輸入域劃分成為等價類,從等價類中導出測試用例
例:乘法計算 BigInteger × BigInteger -> BigInteger 函數,可從正/負角度進行等價類劃分,同時考慮邊界條件—— 0,1,-1,很小的正整數,很小的負整數,很大的正整數,很大的負整數
(注:等價類劃分時,錯誤數據也要考慮其中)?
BVA(Boundary Value Analysis)邊界值分析:是對等價類劃分方法的補充
在等價類劃分時,將邊界作為等價類之一加入考慮
(等價類劃分具體寫法可參見習題課)
健壯性和正確性
可靠性 = 正確性 + 健壯性
健壯性:面向用戶 正確性:面向開發者
private方法可只保證正確性,但面向用戶的還需要保證健壯性
錯誤和異常(Error and Exception)
Error:不是由程序本身引起,由系統限制引起
Exception:自己程序導致的問題,可以捕獲、處理
下面綠色的部分表示是由用戶輸入等引起的,是可預測的,在程序運行時處理
不需要實例化Error,也不需要捕獲(捕獲了也處理不了)
異常分為:運行時異常(RuntimeException)和其他異常
運行時異常是程序員代碼里處理不當造成,其他異常由外部原因造成
Checked and unchecked exceptions
Unchecked exceptions = Error + RuntimeExceptions
兩者區分:編譯器是否能檢查出(編譯器不會檢查Unchecked exception)
checked exception 必須捕獲并指定錯誤處理器handler,否則編譯無法通過
五個處理異常時使用的關鍵字:try,catch,finally,throws,throw
Unchecked異常也能用try/catch來進行捕獲,但大多數時時不需要的,也不應該這樣做——掩耳盜鈴,對發現的編程錯誤充耳不聞!
盡量用unchecked exception來處理編程錯誤——使代碼更易讀
錯誤可預料,不可預防,但有手段從中恢復,用checked exception
(該表需要記住)
規約中需要包含所有該方法拋出的checked exception
異常的拋出需要滿足LSP原則(協變):子類不能比父類拋出更多、更寬泛的異常
可自定義異常類
異常發生后如果找不到處理器,就終止程序,在控制臺打印出 stack trace
異常只有兩種處理方法:向上拋 / 捕獲
如果父類型的方法沒有拋出異常,那么子類型中的方法必須捕獲所有的checked exception
try- catch -finally:無論是否出現異常,finally塊中包含的語句都會被執行(一般為對資源的釋放、管理等)
多個catch塊不是依次順序執行的,而是并發的,哪一個最匹配就執行哪一個
finally會在執行完try/catch塊之后再執行
斷言(assert)
可盡早發現bug,避免擴散
斷言用在開發階段,運行時可被一次性關閉。斷言保證的是正確性。
異常可用于處理用戶輸入錯誤,用在release階段,提高健壯性
assert可用來限定:內部不變量、表示不變量、控制流不變量、前置條件、后置條件等
第三節 構造過程與配置管理
軟件的過程模型
(會考察軟件使用哪一種開發模型)
兩種基本形式:Linear-線性過程 Iterative-迭代過程
五種模型:瀑布過程、增量過程、V字過程、原型過程、螺旋模型
1. 瀑布過程(Waterfall)
即簡單的線性過程,無迭代。雖然管理簡單但無法適應需求增加/變化。
2. 增量過程(Incremental)
是多個瀑布的串行。要求每一段增量都是可運行的,第二個增量不能影響第一個增量(即要求接口必須簡單清晰)。較容易適應需求的增加。
3. V字過程(V-Model)
是瀑布過程的擴展,主要強調每一個階段都要進行測試。
4. 原型過程(Prototyping)
在原型上持續不斷地迭代,發現用戶的需求變化。時間代價高,開發質量也高。
適用于用戶需求不穩定的情況。 缺點:可能注重原型而忽略了系統的架構設計。
5. 螺旋過程(Spiral)
多輪迭代,進行嚴格的風險分析。 適用于長周期、有風險的大程序。
6. 敏捷開發(Agile development)
通過快速迭代和小規模的持續改進,快速適應變化。
要求:1. 強調交互 2. 不需要文檔 3. 合作 4. 變化
適用于需求不穩定,快速開發(對高質量、高風險不適用)
SCM(軟件配置管理)
軟件配置管理:追蹤和控制軟件的變化。 軟件配置項:軟件中發生變化的基本單元
VCS(版本控制系統)
*Git(重點)
Git的四個工作區域
需要掌握根據Git文件的狀態來判斷處于哪一目錄/區域中。
Object Graph
邊 A -> B表示在版本B的基礎上作變化形成了版本A(指向的對象是父對象)
- 除了最初的commit,每個commit都有一個指向父親的指針
- 多個commit指向同一個父親——分支
- 一個commit指向兩個父親——合并
Git中一個子對象只能有0,1,2個父對象,而一個父對象可以有多個子對象。
Git和傳統版本控制工具的區別:Git存儲的是變化后的文件,傳統VCS存儲版本之間的變化(行),很難創建分支。
Git一個文件可以存在在不同的版本中。
Git命令和版本圖
git commit -a :把所有的change先add然后再commit
git fetch :從遠程獲取最新版本到本地,不會自動merge
git checkout -b:創建并切換分支
git remote add origin … :與遠程倉庫關聯
注:git會強制在push之前fetch(如果遠程端做了更改),然后再merge和push
第四節 數據類型和類型檢驗
基本數據類型/對象數據類型
靜態/動態類型檢查
(Java是靜態類型檢查,在編譯階段進行檢查,Java不進行動態類型檢測)
靜態類型檢查:語法、類名/函數名、參數數目、參數類型、返回值類型
動態類型檢查:非法的參數值、非法的返回值、越界、空指針
注意List<String>和List<Object>是在靜態類型檢測中報錯。
Mutable/Immutable
Java可進行自動垃圾回收。 Immutable好處:安全,但浪費空間。
*final特性: final 限定的是引用不變(如果mutable改變值不會報錯),final類無法派生子類,final方法無法被子類重寫。
使用Mutable可獲得更好的性能,也適合多個模塊間共享數據,但不夠安全!
Date也是mutable類!避免使用!
可以使用java.time包中的其他immutable類型的類:LocalDateTime, Instant等
immutable拷貝時間 O(n2)
傳參數盡量用immutable類型(保證參數不變性),如果傳mutable參數可先進行defensive copying(考試經常考)
必須通過類中的方法來改變類中的屬性(防止信息泄露)
*Snapshot diagram(重點)
Immutable對象:用雙線橢圓
不可變的引用(用final修飾的變量):用雙線箭頭
String s1 = new String("abc"); List<String> list = new ArrayList<String>(); list.add(s1); s1 = s1.concat("d"); System.out.println(list.get(0)); String s2 = s1.concat("e"); list.set(0, s2); System.out.println(list.get(0));Array and Collections
Iterator
mutable類型,有兩種方法:next()和hasNext(),next()方法是mutate的
需要注意,當用Iterator迭代List中元素,涉及到remove時,由于remove后List內元素索引會發生改變,會出現錯誤。
Collections
基本類型及其封裝對象類型都是immutable的
List、Map、ArrayList等都是mutable的
可以利用Collections類提供的方法將mutable類包裝成immutable
Collections.unmodifiableList Collections.unmodifiableSet Collections.unmodifiableMap
這種包裝器得到的結果是不可變的,只能看,不能修改(其實就是disabled了一些mutate方法或者讓其拋出異常)
- 這種”不可變“是在運行階段獲得的,編譯階段無法對此進行靜態檢查
- 雖然不能用包裝后的對象對其進行修改,但依舊能用包裝前的對象進行修改
第五節 設計規約
規約(Specifications)
規約不要給出任何方法的實現。
規約不能被程序進行檢測 (×) ——函數描述可以被檢測(參數類型),注釋不能被檢測。
規約注釋包含:功能描述、輸入數據限制、返回值
行為等價性(Behavioral equivalence)
一般站在用戶(客戶端)的角度看(可能會給一定前提),可根據規約判斷是否行為等價
前置條件和后置條件
前置條件,關鍵詞requires 后置條件,關鍵詞effects
前置條件是對客戶端的約束,在使用方法時必須滿足的條件;后置條件是對開發者的約束,方法結束時必須滿足的條件
如果前置條件滿足,后置條件必須滿足;如果前置條件不滿足,后置條件想淦神魔都可以
Java中的規約
Java中的靜態類型聲明是一種規約,可據此進行靜態類型檢查static checking
方法前的注釋也是一種規約,但需要人工判定其是否滿足
前置條件在 @param中,后置條件在 @return和@throws中(@return中不能包含具體類型,如 @return boolean)
如果方法對輸入的參數做了改變,一定要在規約中說明
*規約的強弱(本節重點)
判定標準
規約強度 S2 >= S1 : 前置條件更弱,后置條件更強
spec變強,即更放松的前置條件 + 更嚴格的后置條件
(考試常出無法比較的規約)
Diagramming specifications
每一個點代表一個方法的實現。如果某個具體實現滿足規約,就落在其范圍內;否則在其之外。
更強的規約,表示為更小的區域。(實現的自由度小,面積小)
第六節 抽象數據類型(ADT)
ADT 的特性:表示泄漏、抽象函數 AF 、表示不變量 RI
基于數學的形式對 ADT 的這些核心特征進行描述并應用于設計中
(本節常考大題,包括是否出現表示暴露,AF和RI等)
ADT是由操作定義的,與其內部實現無關
ADT四種操作類型
- 構造器(Creator):創建一個該類型的新對象(可能實現為構造函數或靜態函數)
- 生產器(Producer):從一個類型的舊對象創建一個新對象(如String中concat方法)
- 觀察器(Observer):返回一個不同類型的對象(如List中的size方法)
- 變值器(Mutator):改變對象屬性的方法(如List中的add方法)
如果一個構造器是用靜態方法來實現的,通常稱為工廠方法 (factory method):如Java中String類的String.valueOf(Object obj)方法
Mutators通常返回void,但也可以返回非空類型,如Set.add()返回的類型為boolean
immutable類型的ADT無變值器(Mutator)
判斷是哪種操作類型,首先需要確定是mutable還是immutable
注:Collections.unmodifiableList() 是 producer; 如果一個方法既改變了對象屬性,也返回了不同類型的對象,它是變值器Mutator
*表示獨立性(Representation Independence)
表示獨立性:client使用ADT時無需考慮其內部如何實現,ADT內部表示的變化不應該影響外部spec和客戶端。
上述實例就違反了表示獨立性,因為public限定了這是一個instance variable,但是后面的final又限定了客戶端不能夠實際改變這個類的immutability屬性
*不變性(Invariants)
由ADT來負責其不變量,與client端的任何行為無關
(考試必考表示泄露)
表示泄露出現情況:
當復制代價很高時,可在規約中強加條件(但是不推薦!)
*Rep Invariant and Abstraction Function(RI and AF)
R : 表示空間 A:抽象空間(ADT開發者關注表示空間R,client關注抽象空間A)
R -> A 的映射:1. 滿射:所有抽象值都要有一個rep value 2. 未必單射:一個抽象值可能有多個表示 3. 未必雙射:不是所有的表示值都有對應的抽象值
抽象函數(AF):R和A之間映射關系的函數
表示不變性 RI:某個具體的“表示”是否是“合法的”(R -> boolean)
在ADT的規約里若出現"值",也只能是A空間的"值"
有益的可變性(Beneficent mutation)
(該部分大概率會出選擇)
即immutable的屬性是可變的,但是要保證用戶角度是一樣的
例如:[1, 2] 和 [2, 4]在A空間可均表示1/2
書寫AF和RI
可用ADT的不變量來代替前置條件(相當于將復雜的precondition封裝到了ADT內部)
第七節 面向對象的編程(OOP)
靜態/實例方法
在類中使用static修飾的靜態方法會隨著類的定義而被分配和裝載入內存中;而非靜態方法屬于對象的具體實例,只有在類的對象創建時在對象的內存中才有這個方法的代碼段
編譯器只為整個類創建了一個靜態變量的副本,也就是只分配一個內存空間,雖然可能有多個實例,但這些實例共享該內存
接口(Interface)
接口之間可以繼承與擴展,一個類可以實現多個接口,一個接口可以有多種實現類
接口:確定ADT規約; 類:實現ADT
Java的接口中不能含有constructors,但是從Java 8開始接口中可以含有static工廠方法,可用其替代constructors
default
通過default方法,可以在接口中統一實現某些功能,無需在各個類中重復實現它。好處是以增量式為接口增加額外的功能而不破壞已經實現的類
重寫(Overriding)
嚴格繼承:子類只能添加新方法,無法重寫超類中的方法
如果想要一個java中方法不能被重寫,必須要加上前綴final
- 父類型中的被重寫函數體不為空:意味著對其大多數子類型來說,該方法是可以被直接復用的。對某些子類型來說,有特殊性,故重寫父類型中的函數,實現自己的特殊要求
- 如果父類型中的某個函數實現體為空,意味著其所有子類型都需要這個功能,但各有差異,沒有共性,在每個子類中均需要重寫
重寫時,可以利用super()來復用父類型中函數的功能
抽象類(Abstract Class)
抽象方法:只有聲明沒有具體實現的方法。用關鍵詞abstract來定義
抽象類:如果一個類含有至少一個抽象方法,則被稱為抽象類
接口:一個只含有抽象方法的抽象類
如果某些操作是子類型都共有,但彼此有差別,可以在父類型中設計抽象方法,在各子類型中重寫
接口和抽象類都不能實例化!
多態、子類型、重載(Polymorphism, subtyping and overloading)
(考試經常出現重載和重寫的對比考察)
多態的三種類型
特殊多態和重載(Overloading)
重載:多個方法具有同樣的名字,但有不同的參數列表或返回值類型
重載是一種靜態多態,根據參數列表進行"最佳匹配",進行靜態類型檢查
重載的解析在編譯階段,與之相反,重寫的方法是在運行階段進行動態類型檢查
- 參數列表必須不同
- 相同/不同的返回值類型
- 相同/不同的public/private/protected
- 可以聲明新的異常
Overloading和Overriding的對比
參數多態和泛型(Generic)
泛型擦除:運行時泛型類型消除(如:List<String>運行時是不知道String的),所以,不能使用泛型數組(如: Pair < String >[] foo = new Pair < String >[42]; 是錯誤的!不能被編譯!)
如下是一個錯誤的實例:
List<Object> a; List<String> b; a = b;
通配符(Wildcards),只在使用泛型的時候出現,不能在定義中出現。 如:List< ? extends Animal >
?extends T 和 ?super T 分別表示T和它的所有子/父類
子類型多態、繼承
(詳情見LSP原則)
注:Java無法檢測1,但是可以檢測出2、3
子類型多態:不同類型的對象可以統一的處理而無需區分。
instanceof
instanceof()判斷對象運行時的類型
注:其父類也會判為true,如 a instanceof Object 始終為true
getclass()獲取當前類型
- List<Object>不是List<String>的父類
- List<String>是ArrayList<String>的父類
- List<?> 是 List<String>的父類
注:重寫equal()方法時,需要注意參數類型,必須也是Object類型
第八節 ADT和OOP中的相等性
相等關系
相等關系是一種等價關系,即滿足自反、對稱、傳遞
可以用"是否為等價關系"來檢驗equals()是否正確
Immutable類型的相等
判相等要從A空間來看(用戶角度) AF映射到相同結果,則等價
站在外部觀察者角度:對兩個對象調用任何相同的操作,都會得到相同的結果,則認為這兩個對象是等價的。
== vs. equals()
- == 表示的是引用等價性(一般用于基本數據類型的相等判定)
- equals()表示的是對象等價性 (用于對象類型相等判定)
在自定義ADT時,需要重寫Object 的 equals() 方法
equals()方法的實現
在Objects中實現的缺省equals()是在判斷引用相等性(相當于==)
用instanceof操作可以判斷對象是否是一種特殊的類型(用instanceof是一種動態類型檢查,而不是靜態類型檢查)
注意:不能在父類中用instanceof判斷子類類型
- 等價的對象必須擁有相同的hashCode;不相等的對象也可以映射為同樣的hashCode,但是性能會變差
- 重寫equals方法必須要重寫hashCode方法(除非能保證你的ADT不會被放入到Hash類型的集合中)
mutable類型的相等
觀察等價性:在不改變狀態的情況下,兩個mutable對象是否看起來一致
行為等價性:調用對象的任何方法都展示出一致的結果
對于mutable類型來說,往往傾向于實現嚴格的觀察等價性(但是在有些時候,觀察等價性可能導致bug,甚至破壞RI)
注意:如果某個mutable的對象包含在Set集合類中,當其發生改變后,集合類的行為不確定!
Collections 使用的是觀察等價性,但是其他的mutable類(如StringBuilder)使用的是行為等價性
對mutable類型,實現行為等價性即可。也就是說只有指向同樣內存空間的objects,才是相等的,所以對mutable類型來說,無需重寫這兩個函數,直接調用Object的兩個方法即可。(如果一定要判斷兩個對象"看起來"是否一致,最好定義一個新方法,e.g. similar() )
- immutable類型必須重寫equals() 和 hashCode()
- mutable類型可以不重寫,直接繼承自Object
clone()
clone()創建并返回對象的一個copy
淺拷貝:對于基本數據類型,無影響;對于數組或對象數據類型,淺拷貝只是將內存地址賦值給了新變量,它們指向同一個內存空間。改變其中一個對另一個也會產生影響。
Java中的clone實現的是淺拷貝。
要避免一些問題,建議使用深拷貝。
Autoboxing
Integer 和 int 注意區別
flase
(Numbers between -128 and 127 are true.)
第九節 面向復用的軟件構造技術
(重點:LSP和組合與委托)(考試大部分為小題,LSP會出大題)
復用的類型
軟件復用:最主要的是代碼復用,但也有其他方面。
Source code level:methods, statements, etc Module level:class and interface Library level:API Architecture level:framework
白盒復用:源代碼可見、可修改和擴展(對應繼承)
黑盒復用:源代碼不可見,不能修改,只能通過API接口使用(對應委托)
代碼復用
即直接復制代碼(不推薦)
類的復用
inheritance 繼承 delegation 委托
繼承能做的事委托也能做,繼承要求嚴格父子關系
框架(framework)
框架:一組具體類、抽象類、及其之間的連接關系
開發者根據framework的規約,填充自己的代碼進去,形成完整系統
API和框架的區別:主控端在用戶/框架
*LSP(重點)
子類型多態:客戶端可以用統一的方式處理不同類型的對象。
LSP原則
能被Java靜態類型檢測檢測出的:
- 子類型可以增加方法,但不可以刪除方法
- 子類型需要實現抽象類型中的所有未實現的方法
- 子類型中重寫的方法必須返回相同的類型或者子類型(滿足協變)
- 子類型中重寫的方法必須使用同樣類型的參數(或符合逆變的參數)
- 子類型中重寫的方法不能拋出額外的異常(協變)
不能被靜態類型檢測出的:
- 更強的不變量
- 更弱的前置條件
- 更強的后置條件(與上條綜合即規約更強)
協變:父類型->子類型:越來越具體
反協變(逆變):越來越抽象
注意:Java不支持反協變!Java識別其為重載(而非重寫)
數組滿足協變。
泛型中的LSP
泛型不滿足協變 List<String>不是List<Object>的子類型
Object是所有泛型的父類,List<?>是List<Object>的父類
(該圖為考點!)
委托(Delegation)
Interface Comparator< T >
int compare(T o1, T o2): Compares its two arguments for order
如果你的ADT需要比較大小,或者要放入Collections或Arrays進行排序,可以實現Comparator接口并且override compare()函數
另一種方法:讓ADT實現Comparable接口,然后override compareTo() 方法
與使用Comparator的區別:不需要構建新的Comparator類,比較代碼放在ADT內部
委托
委托/委派:一個對象請求另一個對象的功能。
如果子類只需要復用父類中的一小部分方法,可以通過委托機制調用。
委托是復用的一種常用形式。(CRP原則:盡量使用委托進行復用)
- Use 使用:通過方法的參數傳遞(use_a)
- Association 關聯:通過類的屬性傳遞(has_a)
composition/aggregation 組合/聚合(可認為是Association的兩種具體形態)
聚合運行時可更改綁定對象(較弱的關聯)
聚合B類銷毀時,A類可能不會銷毀(可能還有指向其的指針);組合B類銷毀時,A類同時被銷毀
第十節 面向可維護性的軟件構造技術
(本章重點:SOLID、正則表達式)
可維護性度量指標
圈復雜度(Cyclomatic Complexity)、代碼行數、可維護性指數(MI)、繼承的層次數、類之間的耦合度、單元測試的覆蓋度
模塊化編程
高內聚(High cohension) 低耦合(Low coupling)
耦合(Coupling):不同模塊之間的相互依賴性
內聚(Cohension):模塊內功能和職責的一致性
耦合和內聚之間的權衡:
即不能同時高/同時低
SOLID設計原則
- SRP(單一責任原則)
- OCP(開放-封閉原則)
- LSP(Liskov替換原則)
- DIP(依賴轉置原則)
- ISP(接口聚合原則)
SRP(單一責任原則)
把類拆分,使得每個類只完成一個功能。(不應該有多于一個的原因使得類發生變化)
SRP是最簡單的原則,卻是最難做好的原則
OCP(開放/封閉原則)
對擴展性的開放:模塊的行為應該是可擴展的
對修改的封閉
關鍵的解決方案:抽象技術
例:如果有多種類型的Server,那么針對每一種新出現的Server,不得不修改Server類的內部具體實現;
通過構造一個抽象的Server類:AbstractServer,該抽象類中包含針對所有類型的Server都通用的代碼,從而實現了對修改的封閉;當出現新的Server類型時,只需從該抽象類中派生出具體的子類ConcreteServer即可,從而支持了對擴展的開放。
LSP(Liskov替換原則)
詳情見第九節LSP原則
ISP(接口隔離原則)
盡量和專用接口連接(客戶端不應依賴于它們不需要的方法)
DIP(依賴轉置原則)
盡量依賴抽象類而不是具體類
也就是delegation的時候,通過interface來建立聯系,而非具體子類
正則表達式
*:重復0-多次
|:選擇 a|b
?:0次或1次 x ::= y ? x為y或空串
[a-c]:‘a’ | ‘b’ | ‘c’
[^a-c]:‘d’ | ‘e’ | ‘f’ …(相當于補)
Parse Tree
正則語法(Regular grammar)
正則語法:簡化之后可以表達為一個產生式而不包含任何非終止節點
正則表達式:左側為非終結符,右側沒有非終結符
注意有的字符需要進行轉義
Java中的正則表達式
Pattern是對regex正則表達式進行編譯之后得到的結果
Matcher:利用Pattern對輸入字符串進行解析
Matcher對象只能通過Pattern靜態方法創建,不能new
第十一節 設計模式
分為以下三類模式:創建型模式、結構型模式、行為型模式
創建型模式(Creational patterns)
*工廠方法模式(Factory Method pattern)
當client不知道要創建哪個具體類的實例,或者不想再client代碼中指明要具體創建的實例時,用工廠方法。
工廠模式:將創建一個對象的方法委托給另一個類(工廠類)來實現
Client用工廠方法來創建實例,得到的實例類型是抽象接口而非具體類
靜態工廠方法(在工廠方法前加static):既可以在ADT內部實現,也可以單獨創建工廠類
該設計模式是OCP(擴展/開放原則)的一個體現。
優點:實現信息隱藏
缺點:需要額外創建工廠類,程序更復雜
結構型模式(Structural patterns)
適配器模式(Adapter)
將某個類/接口轉換為client期望的其他形式(主要解決接口不匹配問題)
通過增加一個接口,將已存在的子類封裝起來,client面向接口編程,從而隱藏了具體子類
裝飾器模式(Decorator)
對一個類的功能進行擴充(實現特性的組合)
裝飾器在運行時組合特性;繼承在編譯時組合特性
行為型模式(Behavioral patterns)
*策略模式(Strategy)
使用功能的時候不訪問功能實現,訪問接口(在多個功能間靈活切換)
考試時一般要做的:1. 抽象出一個接口類 2. 調用接口
*模板模式(Template Method)
共性的操作步驟在抽象類中公共實現,差異化的步驟在各個子類中實現
使用繼承和重寫來實現模板模式
模板模式在框架中應用廣泛
抽象類中有一些方法用final來修飾,這些方法即為模板方法
迭代器(Iterator)
將集合類的迭代操作委托給迭代器來實現。
讓自己的集合類實現 Iterable 接口,并實現自己的獨特 Iterator 迭代器 (hasNext, next, remove) ,允許客戶端利用這個迭代器進行顯式或隱式的迭代遍歷:
*Visitor模式
把類中的某些功能委托給別人實現(實現功能時要反過來用到原來的類)
Visitor vs Iterator
迭代器:以遍歷的方式訪問集合數據而無需暴露其內部表示,將“遍歷”這項功能delegate到外部的iterator對象。
Visitor:在特定ADT上執行某種特定操作,但該操作不在ADT內部實現,而是delegate到獨立的visitor對象,客戶端可靈活擴展/改變visitor的操作算法,而不影響ADT
迭代器和Vistor模式結構相同,只是方法不同(本質上無區別)
Strategy vs visitor
自己的獨特 Iterator 迭代器 (hasNext, next, remove) ,允許客戶端利用這個迭代器進行顯式或隱式的迭代遍歷:
總結
以上是生活随笔為你收集整理的2021哈工大软件构造期末考点复习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《小乌龟投资智慧:如何在投资中以弱胜强》
- 下一篇: vs2013 无法创建项目 终极解决方案