日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2021哈工大软件构造期末考点复习笔记

發布時間:2024/1/1 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的四個工作區域

  • 工作區,即平時存放代碼的地方。
  • 暫存區,虛擬區域,無真實空間,用于臨時存放改動。
  • 本地倉庫,安全存放數據的位置,這里有提交到所有版本的數據
  • 遠程倉庫,托管代碼的服務器,如Github
  • 需要掌握根據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端的任何行為無關

    (考試必考表示泄露)

    表示泄露出現情況:

  • public 類型的數據 -> private final
  • mutable 類型共享引用
  • 不應該包含mutate方法
  • 當復制代價很高時,可在規約中強加條件(但是不推薦!)

    *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)

    (考試經常出現重載和重寫的對比考察)

    多態的三種類型
  • 特殊多態(Ad hoc polymorphism):重載
  • 參數化多態(Parametric polymorphism):泛型
  • 子類型多態、包含多態(Subtyping):繼承
  • 特殊多態和重載(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和它的所有子/父類

    子類型多態、繼承
  • 重寫時,子類的規約要強于父類的規約(更弱的前置條件,更強的后置條件)
  • 子類的可見性要強于父類(即父類如果是public,子類不能為private)
  • 子類不能比父類拋出更多的異常
  • (詳情見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)
    class B{void b(A a){ //use 使用... } } class B{A a; //Association 關聯.... }

    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哈工大软件构造期末考点复习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。