[閱讀] 編寫程式的邏輯: 如何用物件導向實作複雜的業務需求
ISBN 978-986-434-784-1 (1.物件導向 2.軟體研發)
物件導向理論
類別(class)
概念:一組相似事物的統稱
- 一組:多個
- 相似:比較像,但不完全相同
- 統稱:通稱,概括多個事物
劃分方法:站在個人的觀察角度,察覺有相似的事物就是同一類
組成:
- 屬性:類別的特性
- 原則:最小化原則,將屬性切到無法在分割為止
- 方法:類別的功能
- 原則:單一化原則,一個方法只做一件事
- 屬性:類別的特性
物件(object)
概念:一個具體、真實存在的類別
(軟體)物件產生的流程方法 (水果 -> 蘋果、香蕉)
- 現實物件:可觀察到現實存在的物體
- 現實類別:對現實物體的歸納總結
- 軟體物件:軟體運行時的實體
- 軟體類別:軟體設計過程中的歸納總結而成的分類
- 觀察現實存在的物件,歸納概括成現實的類別
- 根據現實的類別,在軟體設計的過程中,歸納總結的分類成果
- 實體化在軟體運行中的實體
實作技巧
- 概念:電影來自生活,但高於生活 => 軟體類別來自現實類別,但高於現實類別
- 可以用多個軟體類別,組成一個對應的現實類別 (可不用一對一)
- eg: (現實類別) ATM 功能 = (軟體類別) ATM 餘額查詢 + ATM 取款 + ATM 存款
- 軟體類別因為是抽象模擬的 “概念”,所以並不一定存於現實中
- eg: 策略 strategy,工作流程 workflow
介面(interface)
概念:一組相關、有互動功能的點集合
- inter:互相,兩方的互動
- face:面,點的集合,相對應於點、線
定義概念拆解:
- 相關:彼此相關的功能作用,eg: USB 介面、網路介面、
- 互動:可作用於不同物體之間的互動,非單一方面
- 定義:只做定義,但不涉具體實作 => 一個互動雙方的“約定”,具體的實作方式由實際互動方制定即可
- 集合:多個功能點的集合,非一個具體的功能點
特性:
- 在希望某群物件有共同的屬性或功能下使用
- 在不清楚某群物件是否屬於同一個類別下使用
- eg: 在面對處理的物體是動物,但又希望這個動物按照所需求的方式進行活動 (人、狗、其他動物)
抽象(abstract)
概念:抽取比較像的部分出來
結合物件的概念:抽取多個物件或類別中比較像的部分
特性:
- 可劃分具有“相似”的類別
- 主要目的:隔離關注點,降低複雜度
- 主要用於發現新類別的方法 => 先抽象共同點,在拉出一個新的類別
- eg: (developer,maintainer)=> people, (developer,dog) => animal
抽象類別(abstract class)
概念:根據類別共有的特性,再進行抽取比較像的部分
特性:
- 本質上還是類別,但只能用於繼承,無法用來產生實體的物件
- 相對於普通的軟體類別由現實類別抽象模擬而出,抽象類別則是更高級的抽象
- 抽象類別是由普通軟體類別抽象模擬而成
- eg: 吃“水果”(抽象類別),實際上是吃“蘋果、香蕉”(普通類別)
- 同時具備普通類別與介面的部分特性,為介於兩者之間的概念
與普通類別的差異:
- 繼承:
- 普通類別:子類別透過繼承父類別,即可獲得父類別的方法
- 抽象類別:若有定義抽象方法(只有宣告,但沒有實作),則子類別需要自己去實作方法
- 繼承:
與介面的差異:
- 概念:
- 介面:強調物件之間的相似性,但僅限於方法宣告,缺少定義上的相似
- 抽象類別:為類別,強調一組事物的相似性,包含屬性與方法
- 實作:
- 介面:需要逐一實作個別物件的屬性與方法
- 抽象類別:若有定義抽象方法,則需要為繼承的物件個別實作
- 概念:
物件導向核心特徵
封裝 (encapsulation)
- 概念:使用類別,將一組相似事物的特性與行為包裝起來
- 作用:
- 保護隱私
- 對類別內個屬性的修改,只能通過內部的類別方法存取
- 隔離複雜度
- 個別類別只需負責自己的實作功能即可
- 類別外在的使用者,只需呼叫類別提供的方法即可,不需了解其內部的實作方法
- 保護隱私
- 方法:
- public:不進行封裝,直接對外公開
- protected:只對 child,與 friend 類別公開
- private:完全不公開
繼承 (inheritance)
- 概念:遺傳,承襲 parent 的特性與行為
- 與抽象的差異
- 抽象: 分析和設計過程的一個動作、一個技巧,透過抽象出類別
- 繼承: 實作過程的一個動作,根據抽象的結果,透過程式語言的特性,完成抽象的模擬
- 抽象與繼承的關係:“承先啟後”,先有抽象,再由抽象得出類別,最後由繼承表達抽象的結果
多型(polymorphism)
- 概念:“多胎”,使用指向父類別的指標或參考,便能呼叫子類別的物件
- 特性:
- 遮罩對於子物件的差異,允許呼叫者撰寫通用性的程式碼
- 當呼叫函式時,帶入指定為 parent 的類型時,可以帶入 children 的物件屬性,並在內部使用 parent 定義的函式
物件導向方法
物件導向開發流程
需求模型:透過和客戶溝通,結合行業經驗和知識,明確刻畫客戶的需求
領域模型:根據需求模型,提煉出領域相關的概念,為後面的物件導向設計打下基礎
設計模型:以領域模型為基礎,綜合物件導向的各種設計技巧,完成類別的設計
實作模型:以設計模型為基礎,將設計模型轉譯為具體的語言實作,完成程式碼的撰寫
需求模型
需求分析方法 : 5W 1H 8C
- 需求的兩種屬性
- 功能屬性 : 5W 1H
- 品質屬性 : 8C
- 5W (When, Where, Who, What, Why) : 用來描述產生需求的情境或上下文關係
- When : 需求作用的時間, ex : 夜間備份
- Where : 需求作用的地點, ex : 國家區域文化相關
- Who : 需求中會參與到的 “參與者”, 不限於 ‘人‘, 也可以是與外部的系統連結
- What : 需求方最終想要的輸出結果
- Why : 需求方提出需求的原動力或誘因,即遇到的困難、問題、皆包含在內
- 1H (How) : 描述需求的流程,藉由 “使用案例方法” 釐清需求進行流程
- 8C (Constraint) : 需求中的額外限制
- Performance : 系統要求的效能, 使用量負載等
- Cost : 實作系統所需付出的成本,包含可能的沈沒成本
- Time : 需要交付系統的時間
- Reliability : 系統長時間正確運行的能力
- Security : 系統針對資安的防護能力
- Compliance : 系統是否符合該行業中的標準, 法規, 規範等
- Technology : 要求要採用的技術或平台
- Compatibility : 新系統與需求方舊有系統的兼容性
- 需求的兩種屬性
使用案例 (How 的分析方法)
- NEA 三段案例分析方法
- Normal (正常流程) : 需求一般執行流程
- Exception (異常處理流程) : 在正常流程中,可能有的各種異常狀態與應對方式,分系統本身之異常
- Alternative (替代處理流程) : 在正常流程中,是否有其他替代方法與對應進行方式
- 使用案例寫法大綱 for 各個 N -> A -> E
- 需求名稱
- 場景 (Who, Where, When)
- 詳細描述案例 (What, How) : 如何實行, 實行步驟
- 對應客戶價值 (Why)
- 約束與限制 8C (Constraint)
- example for NAE : 賣場支付方式
- Normal : 現金支付
- Expception :客戶現金不足,要求刪除購物車中的一些商品
- Alternative : 改用信用卡支付
- NEA 三段案例分析方法
從使用案例提取功能
- 將使用案例中的「 動詞 」進行標註,轉換成系統中的 「 功能 」
- 製作功能列表 ex:提款
功能編號 功能描述 涉及使用案例 001 銀行卡驗證 提款、存款、餘額查詢 002 點鈔 提款、存款
系統循序圖 (SSD, System Sequence Diagram)
- 為使用案例的視覺化描述圖示,但無法取代使用案例
- 與 UML 使用案例圖差異
- 系統循序圖 : 描述、分析案例
- UML 使用案例圖 : 描述系統
- SSD 使用事項
- 非標準 UML 圖形,UML 只有個別的循序圖與使用案例圖
- 圖中只有兩種物件 : 1. 系統, 2. 與系統互動的物件
- 並非描述系統的整體結構,一張圖只描述使用案例中的其中一個分支
- 只需挑選重要的分支進行繪製即可,不用為每個分支都繪製一張圖
- 省略與系統無關的業務步驟 : ex : 結帳後,把零錢找給客戶
- 轉換業務語言為系統語言 : ex : 顧客提取結帳完的物品離開 => 交易結束
領域模型
基本概念
- 為完成從「需求分析」到「物件導向設計」的橋樑
- 對領域內的概念或現實世界中的物件的視覺化表示
- 與 UML 類別圖相似,但只需標示屬性(不需方法),因為只需專注於描述領域實體即可
主要作用
- 發掘重要的業務領域概念
- 建立業務領域概念之間的關係
領域模型建置順序
- 「找名詞」:從使用案例中找名詞
- 「加屬性」:從名詞中找出相關的屬性,ex: 收銀員 => 國籍、員工編號
- 「連關係」:識別並串連名詞之間的關係 (一對ㄧ 1…1, 一對一 1…n, 多對一 n…1)
設計模型
組成部分
- 靜態模型(類別模型) : 關注於系統的「靜態」結構
- 實質意義:協助類別的宣告
- 描述的範圍:系統的類別、類別的名稱、職責、屬性、方法、類別之間的關係
- 動態模型 : 關注於系統的「動態」行為
- 實質意義:協助類別的實作、與實作過程
- 需使用到 UML 類別圖的類別,並對照使用案例,則一使用合適的動態模型圖即可
- 靜態模型(類別模型) : 關注於系統的「靜態」結構
靜態模型(類別模型)步驟
- 領域類別映射
- 使用「領域模型圖」為基底製作「類別模型圖」
- 類別模型圖製作順序
- 篩選類別:將領域類別轉換成軟體類別,去除非必要的實體
- 提煉方法:從「使用案例模型」中「找動詞」
- 篩選與識別:從「動詞」挑出可成為軟體類別的方法
- 分配:將挑出的「動詞」形成方法,並分配給已經擁有屬性的軟體類別
- 應用設計原則和設計模式
- 拆分輔助類別
- 因應框架或規範需求而拆解輔助類別
- ex: MVC 模式,將一個業務拆解成 Model、View、Control 三個元素
- ex: DAO 物件,將服務與資料庫的相關操作獨立成一個 DAO 物件
- 領域類別映射
動態模型步驟
- 挑選合適的動態模型圖 ex: 狀態圖、序列圖、活動圖、協作圖、
- 建模實踐:從使用案例至產生動態圖的一個「 分解和分配」的過程
- 建模技巧
- 只需針對關鍵、核心、複雜的業務或功能進行設計即可
- 模型不等於虛擬碼,更不等於程式碼,模型只是用來協助程式碼撰寫而已
物件導向技巧
設計原則
內聚(Cohesion)
- 概念:“凝聚力”,模組 (函數、類別、套件、子系統) 內的元素,是否都專注於模組所賦予的職責
- 較佳的內聚形式:功能內聚
- 案例概念:進行 CRUD 操作的類別,內部的元素間雖沒相關互動,但皆專注於資料的操作與存取
耦合(Coupling)、依賴(Dependency)
- 概念:模組間的依賴程度,某個模組A使用了另一個模組B的元素完成特定功能
- 最差的耦合形式:內容耦合
- 概念:模組B修改模組A所依賴的內容(屬性)時,模組A跟模組B都必須同步修改
設計原則:高內聚低耦合
- 目的:降低複雜性
- 反向思維:低內聚 or 高耦合 會使系統產生較多的不確性,即不穩定
- 實作想法:在高內聚與低耦合間取得平衡,不偏向任何一端
類別設計原則 S.O.L.I.D. principle
SRP (Single Responsibility Principle, 單一職責原則)
- 概念:一組類別只需負責一組 “相關” 的事, 即一個類別有多個方法,且相互關聯
- 職責定義:
- 由其他的類別視角來決定
- 包含多個相關的功能
- 適用範圍:基礎類別
- 不適用範圍 : 聚合類別 (根據基礎類別建構的複雜結構)。
- 解法:優先使用多物件作組合,而非類別繼承
- 應用場景:用於類別的設計
OCP (Open-Closed Principle, 開放封閉原則)
- 概念:在使用者不改變操作方法的條件下,提供者增加新的功能
- 含義:對提供者(provider)開放擴充,對使用者(consumer)關閉修改 (open for provider extension, closed for consumer modification)
- 適用原則:定義相同的介面(provider)進行實作(consumer實作),即 “透過介面進行互動”
- 適用範圍:
- 類別之間使用 interface 互動
- 模組和模組、系統和系統之間使用 協定 互動,ex: HTTP, SOAP
- 應用場景:整體架構的設計
- 一般能符合 LSP、ISP、DIP,則可符合 OCP
LSP (Liskov Substitution Principle, Liskov 替換原則)
- 概念:定義子類別物件繼承來自父類別的規範
- 子類別必須實作 or 繼承父類別所有的公有方法 (public method)
- 子類別每個方法的 input 參數必須與父類別方法一致,eg: 應用使用 “多型” 時,大多是定義父類別作為 input 的參數
- 子類別每個方法的 output 必須多於父類別 (至少能完成父類別定義的輸出)
- 適用原則:專注於定義方法相同的 input 與 output,但不包含其中的過程
- 應用場景:用於類別的繼承,與 DIP 相輔相成,由 interface、abstract class 衍生出新的子類別
- 概念:定義子類別物件繼承來自父類別的規範
ISP (Interface Seggregation Principle, 介面隔離原則)
- 概念:類別內有使用到的介面,應該照個別使用需求去呼叫,而非使用單一個大而全面的介面
- 含義:物件內需要非內聚的介面,consumer 不需要知道整個類別,只需要知道有內聚介面的抽象父類別即可
- 適用原則:不應該強迫 consumer 依賴不需要的介面,即 consumer 只使用剛剛好的依賴介面
- 應用場景:用於介面的設計
DIP (Dependency Inversion Principle, 依賴反轉原則)
- 概念:使用抽象的方法,將類別間的依賴、耦合性降低
- 含義:(底層模組:一個完整無法再分割的邏輯,高層模組:表達的邏輯是由多個底層模組成)
- 高層模組不應該直接依賴底層模組,兩個都應該依賴抽象層
- 抽象不能依賴細節,細節(實作)必須依賴抽象(定義)
- 應用場景:用於提取出抽象,即抽出共同、相似的部分,形成 interface、abstract class、