[閱讀] 編寫程式的邏輯: 如何用物件導向實作複雜的業務需求

ISBN 978-986-434-784-1 (1.物件導向 2.軟體研發)

物件導向理論

類別(class)

  • 概念:一組相似事物的統稱

    • 一組:多個
    • 相似:比較像,但不完全相同
    • 統稱:通稱,概括多個事物
  • 劃分方法:站在個人的觀察角度,察覺有相似的事物就是同一類

  • 組成:

    1. 屬性:類別的特性
      • 原則:最小化原則,將屬性切到無法在分割為止
    2. 方法:類別的功能
      • 原則:單一化原則,一個方法只做一件事

物件(object)

  • 概念:一個具體、真實存在的類別

  • (軟體)物件產生的流程方法 (水果 -> 蘋果、香蕉)

    • 現實物件:可觀察到現實存在的物體
    • 現實類別:對現實物體的歸納總結
    • 軟體物件:軟體運行時的實體
    • 軟體類別:軟體設計過程中的歸納總結而成的分類
    1. 觀察現實存在的物件,歸納概括成現實的類別
    2. 根據現實的類別,在軟體設計的過程中,歸納總結的分類成果
    3. 實體化在軟體運行中的實體
  • 實作技巧

    • 概念:電影來自生活,但高於生活 => 軟體類別來自現實類別,但高於現實類別
    • 可以用多個軟體類別,組成一個對應的現實類別 (可不用一對一)
      • eg: (現實類別) ATM 功能 = (軟體類別) ATM 餘額查詢 + ATM 取款 + ATM 存款
    • 軟體類別因為是抽象模擬的 “概念”,所以並不一定存於現實中
      • eg: 策略 strategy,工作流程 workflow

介面(interface)

  • 概念:一組相關、有互動功能的點集合

    • inter:互相,兩方的互動
    • face:面,點的集合,相對應於點、線
  • 定義概念拆解:

    1. 相關:彼此相關的功能作用,eg: USB 介面、網路介面、
    2. 互動:可作用於不同物體之間的互動,非單一方面
    3. 定義:只做定義,但不涉具體實作 => 一個互動雙方的“約定”,具體的實作方式由實際互動方制定即可
    4. 集合:多個功能點的集合,非一個具體的功能點
  • 特性:

    • 在希望某群物件有共同的屬性或功能下使用
    • 在不清楚某群物件是否屬於同一個類別下使用
    • eg: 在面對處理的物體是動物,但又希望這個動物按照所需求的方式進行活動 (人、狗、其他動物)

抽象(abstract)

  • 概念:抽取比較像的部分出來

  • 結合物件的概念:抽取多個物件或類別中比較像的部分

  • 特性:

    • 可劃分具有“相似”的類別
    • 主要目的:隔離關注點,降低複雜度
    • 主要用於發現新類別的方法 => 先抽象共同點,在拉出一個新的類別
    • eg: (developer,maintainer)=> people, (developer,dog) => animal

抽象類別(abstract class)

  • 概念:根據類別共有的特性,再進行抽取比較像的部分

  • 特性:

    • 本質上還是類別,但只能用於繼承,無法用來產生實體的物件
    • 相對於普通的軟體類別由現實類別抽象模擬而出,抽象類別則是更高級的抽象
    • 抽象類別是由普通軟體類別抽象模擬而成
    • eg: 吃“水果”(抽象類別),實際上是吃“蘋果、香蕉”(普通類別)
    • 同時具備普通類別與介面的部分特性,為介於兩者之間的概念
  • 與普通類別的差異:

    • 繼承:
      • 普通類別:子類別透過繼承父類別,即可獲得父類別的方法
      • 抽象類別:若有定義抽象方法(只有宣告,但沒有實作),則子類別需要自己去實作方法
  • 與介面的差異:

    • 概念:
      • 介面:強調物件之間的相似性,但僅限於方法宣告,缺少定義上的相似
      • 抽象類別:為類別,強調一組事物的相似性,包含屬性與方法
    • 實作:
      • 介面:需要逐一實作個別物件的屬性與方法
      • 抽象類別:若有定義抽象方法,則需要為繼承的物件個別實作

物件導向核心特徵

  1. 封裝 (encapsulation)

    • 概念:使用類別,將一組相似事物的特性與行為包裝起來
    • 作用:
      • 保護隱私
        • 對類別內個屬性的修改,只能通過內部的類別方法存取
      • 隔離複雜度
        • 個別類別只需負責自己的實作功能即可
        • 類別外在的使用者,只需呼叫類別提供的方法即可,不需了解其內部的實作方法
    • 方法:
      • public:不進行封裝,直接對外公開
      • protected:只對 child,與 friend 類別公開
      • private:完全不公開
  2. 繼承 (inheritance)

    • 概念:遺傳,承襲 parent 的特性與行為
    • 與抽象的差異
      • 抽象: 分析和設計過程的一個動作、一個技巧,透過抽象出類別
      • 繼承: 實作過程的一個動作,根據抽象的結果,透過程式語言的特性,完成抽象的模擬
      • 抽象與繼承的關係:“承先啟後”,先有抽象,再由抽象得出類別,最後由繼承表達抽象的結果
  3. 多型(polymorphism)

    • 概念:“多胎”,使用指向父類別的指標或參考,便能呼叫子類別的物件
    • 特性:
      • 遮罩對於子物件的差異,允許呼叫者撰寫通用性的程式碼
      • 當呼叫函式時,帶入指定為 parent 的類型時,可以帶入 children 的物件屬性,並在內部使用 parent 定義的函式

物件導向方法

物件導向開發流程

  1. 需求模型:透過和客戶溝通,結合行業經驗和知識,明確刻畫客戶的需求

  2. 領域模型:根據需求模型,提煉出領域相關的概念,為後面的物件導向設計打下基礎

  3. 設計模型:以領域模型為基礎,綜合物件導向的各種設計技巧,完成類別的設計

  4. 實作模型:以設計模型為基礎,將設計模型轉譯為具體的語言實作,完成程式碼的撰寫

需求模型

  1. 需求分析方法 : 5W 1H 8C

    • 需求的兩種屬性
      • 功能屬性 : 5W 1H
      • 品質屬性 : 8C
    • 5W (When, Where, Who, What, Why) : 用來描述產生需求的情境或上下文關係
      1. When : 需求作用的時間, ex : 夜間備份
      2. Where : 需求作用的地點, ex : 國家區域文化相關
      3. Who : 需求中會參與到的 “參與者”, 不限於 ‘人‘, 也可以是與外部的系統連結
      4. What : 需求方最終想要的輸出結果
      5. Why : 需求方提出需求的原動力或誘因,即遇到的困難、問題、皆包含在內
    • 1H (How) : 描述需求的流程,藉由 “使用案例方法” 釐清需求進行流程
    • 8C (Constraint) : 需求中的額外限制
      1. Performance : 系統要求的效能, 使用量負載等
      2. Cost : 實作系統所需付出的成本,包含可能的沈沒成本
      3. Time : 需要交付系統的時間
      4. Reliability : 系統長時間正確運行的能力
      5. Security : 系統針對資安的防護能力
      6. Compliance : 系統是否符合該行業中的標準, 法規, 規範等
      7. Technology : 要求要採用的技術或平台
      8. Compatibility : 新系統與需求方舊有系統的兼容性
  2. 使用案例 (How 的分析方法)

    • NEA 三段案例分析方法
      1. Normal (正常流程) : 需求一般執行流程
      2. Exception (異常處理流程) : 在正常流程中,可能有的各種異常狀態與應對方式,分系統本身之異常
      3. Alternative (替代處理流程) : 在正常流程中,是否有其他替代方法與對應進行方式
    • 使用案例寫法大綱 for 各個 N -> A -> E
      • 需求名稱
      • 場景 (Who, Where, When)
      • 詳細描述案例 (What, How) : 如何實行, 實行步驟
      • 對應客戶價值 (Why)
      • 約束與限制 8C (Constraint)
    • example for NAE : 賣場支付方式
      • Normal : 現金支付
      • Expception :客戶現金不足,要求刪除購物車中的一些商品
      • Alternative : 改用信用卡支付
  3. 從使用案例提取功能

    • 將使用案例中的「 動詞 」進行標註,轉換成系統中的 「 功能 」
    • 製作功能列表 ex:提款
      功能編號功能描述涉及使用案例
      001銀行卡驗證提款、存款、餘額查詢
      002點鈔提款、存款
  4. 系統循序圖 (SSD, System Sequence Diagram)

    • 為使用案例的視覺化描述圖示,但無法取代使用案例
    • 與 UML 使用案例圖差異
      • 系統循序圖 : 描述、分析案例
      • UML 使用案例圖 : 描述系統
    • SSD 使用事項
      • 非標準 UML 圖形,UML 只有個別的循序圖與使用案例圖
      • 圖中只有兩種物件 : 1. 系統, 2. 與系統互動的物件
      • 並非描述系統的整體結構,一張圖只描述使用案例中的其中一個分支
      • 只需挑選重要的分支進行繪製即可,不用為每個分支都繪製一張圖
      • 省略與系統無關的業務步驟 : ex : 結帳後,把零錢找給客戶
      • 轉換業務語言為系統語言 : ex : 顧客提取結帳完的物品離開 => 交易結束

領域模型

  1. 基本概念

    • 為完成從「需求分析」到「物件導向設計」的橋樑
    • 對領域內的概念或現實世界中的物件的視覺化表示
    • 與 UML 類別圖相似,但只需標示屬性(不需方法),因為只需專注於描述領域實體即可
  2. 主要作用

    1. 發掘重要的業務領域概念
    2. 建立業務領域概念之間的關係
  3. 領域模型建置順序

    1. 「找名詞」:從使用案例中找名詞
    2. 「加屬性」:從名詞中找出相關的屬性,ex: 收銀員 => 國籍、員工編號
    3. 「連關係」:識別並串連名詞之間的關係 (一對ㄧ 1…1, 一對一 1…n, 多對一 n…1)

設計模型

  1. 組成部分

    1. 靜態模型(類別模型) : 關注於系統的「靜態」結構
      • 實質意義:協助類別的宣告
      • 描述的範圍:系統的類別、類別的名稱、職責、屬性、方法、類別之間的關係
    2. 動態模型 : 關注於系統的「動態」行為
      • 實質意義:協助類別的實作、與實作過程
      • 需使用到 UML 類別圖的類別,並對照使用案例,則一使用合適的動態模型圖即可
  2. 靜態模型(類別模型)步驟

    1. 領域類別映射
      • 使用「領域模型圖」為基底製作「類別模型圖」
      • 類別模型圖製作順序
        1. 篩選類別:將領域類別轉換成軟體類別,去除非必要的實體
        2. 提煉方法:從「使用案例模型」中「找動詞」
        3. 篩選與識別:從「動詞」挑出可成為軟體類別的方法
        4. 分配:將挑出的「動詞」形成方法,並分配給已經擁有屬性的軟體類別
    2. 應用設計原則和設計模式
    3. 拆分輔助類別
      • 因應框架或規範需求而拆解輔助類別
      • ex: MVC 模式,將一個業務拆解成 Model、View、Control 三個元素
      • ex: DAO 物件,將服務與資料庫的相關操作獨立成一個 DAO 物件
  3. 動態模型步驟

    1. 挑選合適的動態模型圖 ex: 狀態圖、序列圖、活動圖、協作圖、
    2. 建模實踐:從使用案例至產生動態圖的一個「 分解和分配」的過程
    3. 建模技巧
      • 只需針對關鍵、核心、複雜的業務或功能進行設計即可
      • 模型不等於虛擬碼,更不等於程式碼,模型只是用來協助程式碼撰寫而已

物件導向技巧

設計原則

  1. 內聚(Cohesion)

    • 概念:“凝聚力”,模組 (函數、類別、套件、子系統) 內的元素,是否都專注於模組所賦予的職責
    • 較佳的內聚形式:功能內聚
      • 案例概念:進行 CRUD 操作的類別,內部的元素間雖沒相關互動,但皆專注於資料的操作與存取
  2. 耦合(Coupling)、依賴(Dependency)

    • 概念:模組間的依賴程度,某個模組A使用了另一個模組B的元素完成特定功能
    • 最差的耦合形式:內容耦合
      • 概念:模組B修改模組A所依賴的內容(屬性)時,模組A跟模組B都必須同步修改
  3. 設計原則:高內聚低耦合

    • 目的:降低複雜性
    • 反向思維:低內聚 or 高耦合 會使系統產生較多的不確性,即不穩定
    • 實作想法:在高內聚與低耦合間取得平衡,不偏向任何一端

類別設計原則 S.O.L.I.D. principle

  1. SRP (Single Responsibility Principle, 單一職責原則)

    • 概念:一組類別只需負責一組 “相關” 的事, 即一個類別有多個方法,且相互關聯
    • 職責定義:
      1. 由其他的類別視角來決定
      2. 包含多個相關的功能
    • 適用範圍:基礎類別
    • 不適用範圍 : 聚合類別 (根據基礎類別建構的複雜結構)。
      • 解法:優先使用多物件作組合,而非類別繼承
    • 應用場景:用於類別的設計
  2. OCP (Open-Closed Principle, 開放封閉原則)

    • 概念:在使用者不改變操作方法的條件下,提供者增加新的功能
    • 含義:對提供者(provider)開放擴充,對使用者(consumer)關閉修改 (open for provider extension, closed for consumer modification)
    • 適用原則:定義相同的介面(provider)進行實作(consumer實作),即 “透過介面進行互動”
    • 適用範圍:
      • 類別之間使用 interface 互動
      • 模組和模組、系統和系統之間使用 協定 互動,ex: HTTP, SOAP
    • 應用場景:整體架構的設計
      • 一般能符合 LSP、ISP、DIP,則可符合 OCP
  3. LSP (Liskov Substitution Principle, Liskov 替換原則)

    • 概念:定義子類別物件繼承來自父類別的規範
      1. 子類別必須實作 or 繼承父類別所有的公有方法 (public method)
      2. 子類別每個方法的 input 參數必須與父類別方法一致,eg: 應用使用 “多型” 時,大多是定義父類別作為 input 的參數
      3. 子類別每個方法的 output 必須多於父類別 (至少能完成父類別定義的輸出)
    • 適用原則:專注於定義方法相同的 input 與 output,但不包含其中的過程
    • 應用場景:用於類別的繼承,與 DIP 相輔相成,由 interface、abstract class 衍生出新的子類別
  4. ISP (Interface Seggregation Principle, 介面隔離原則)

    • 概念:類別內有使用到的介面,應該照個別使用需求去呼叫,而非使用單一個大而全面的介面
    • 含義:物件內需要非內聚的介面,consumer 不需要知道整個類別,只需要知道有內聚介面的抽象父類別即可
    • 適用原則:不應該強迫 consumer 依賴不需要的介面,即 consumer 只使用剛剛好的依賴介面
    • 應用場景:用於介面的設計
  5. DIP (Dependency Inversion Principle, 依賴反轉原則)

    • 概念:使用抽象的方法,將類別間的依賴、耦合性降低
    • 含義:(底層模組:一個完整無法再分割的邏輯,高層模組:表達的邏輯是由多個底層模組成)
      1. 高層模組不應該直接依賴底層模組,兩個都應該依賴抽象層
      2. 抽象不能依賴細節,細節(實作)必須依賴抽象(定義)
    • 應用場景:用於提取出抽象,即抽出共同、相似的部分,形成 interface、abstract class、
0%