{%hackmd DfWYF9cYREebVNN1eEOz-w %} 物件導向 個人心得(2 ====== > ***不知自己不知道, 那你會以為你知道.*** ###### tags: `OOP` `個人心得` [**在 2018 年左右建立的觀念(參考此篇**](https://dotblogs.com.tw/Kevin_Date/2018/03/29/165914) 前言 ------ &nbsp;&nbsp;&nbsp;&nbsp;主要記錄從 2018~2021 三年來關於物件導向的觀念上是否有什麼改變, 所以想在補充一點資訊後產生此篇. 此篇參考的資訊: [程式如何正確撰寫 ? | 物件導向程式設計 - SOLID 設計原則 : SRP、OCP、LSP、ISP、DIP](https://devs.tw/post/439) [Laravel、Ruby on Rails、Django ... 使用 Active Record Pattern 框架的最大問題:到底該把 save 寫在哪裡?](https://devs.tw/post/328) [OOP 程式開發:用一個簡單的數學公式來幫忙設計類別](https://devs.tw/post/340) [元件如何正確使用 ? | 元件耦合性三大原則 : ADP、SDP、SAP](https://devs.tw/post/385) [運算思維的核心 - 抽象化](https://medium.com/orangeapple/%E9%81%8B%E7%AE%97%E6%80%9D%E7%B6%AD%E7%9A%84%E6%A0%B8%E5%BF%83-%E6%8A%BD%E8%B1%A1%E5%8C%96-c7e013f30c6) ------ ### 「用一個簡單的數學公式來幫忙設計類別」 * 解決 God Object 上帝物件 * 把相關的 property 跟 method 抽出來, 很輕易就能拆出多個 CR 更高的類別. * 當你這麼做的同時通常很神奇地,類別命名會自然浮現. * Long Method 過長的解決方法, 可分兩步驟改寫```(前提: 只在職責較為清晰的類別用以下步驟, 因職責不明確的類別通常是 God Object 若用以下步驟抽為屬性時, 通常會難以辨識屬性的定位, 更多的是模糊關注點.)``` * 步驟一 * 先把每段任務拆成各自的多個小 method, 把用到的變數傳進去 * 你會發現同樣的好幾組變數, 反覆傳到好幾個 method, 似乎變得更糟糕了 * 步驟二 * 這些反覆傳送的參數, 變成類別的 property * 你會發現很多 method 不再依靠參數, 而是轉跟 property 互動 * 因這些 property 反覆在多個 method 被使用, 這時 CR 也跟著提高了 * 內聚係數 (Cohesion Ratio) CR 越高越好: ``` (每個方法用到的屬性數量加總)/(屬性的數量 * 方法的數量) ``` --- ### SOLID 設計原則 : SRP、OCP、LSP、ISP、DIP * SRP * 一個模組應該只對唯一的一個角色負責 * 單一職責實際上是一種「分類」的方法,依據的是「不同角色的使用者」、「以不同視角的觀察」。 * OCP * 一樓已經完成, 若需追加新的設計只能從二樓開始 * 不應再為了某種需求在一樓的牆壁鑽孔、打洞 * 萬一剛好是某個重要結構,可能導致傾斜或倒塌,這樣肯定得不償失 * LSP(要有繼承關係) * 若對於型態 S 每個物件 o1 都存在一個型態為 T 的物件 o2 * 使得在所有針對 T 編寫的程式 P 中, 用 o1 替代 o2 後, 程式 P 的行為功能不變, 則 S 是 T 的子型態 * **上述簡而化之就是,子類別能替代父類別**。如果發現子類時常覆寫父類方法,或是父類的方法改成可覆寫。就要注意子類別是否分類錯誤,或者父類別定義模糊 ...等因素導致。 * ISP * 不應強迫客戶端依賴它不使用的方法 * DIP * 高層次的模組不應該依賴於低層次的模組, 兩者都應該依賴於抽象介面 --- ### 還有「元件的內聚性原則」與「元件間的耦合性原則」 * SDP 穩定依賴原則 (stable dependences principle) * 穩定的元件 ? * 本身元件的類別被外部元件或功能所依賴 * 本身元件中的類別不依賴外部元件或功能的類別 * 不容易被依賴於自身的外部元件或功能給影響 * 因需求變動或新增時, 進而影響依賴的外部元件或功能 * 不穩定的元件 ? * 本身元件沒有被外部元件或功能依賴, 但依賴了很多外部的元件 * 外部的元件更動, 都有可能受到影響本身元件 * 但本身元件的改動, 卻不會影響到其他元件 * 修改該元件的主因, 都因其它元件修改時才改動 * 穩定度計算公式 => 不穩定性 I (instablility) = FanOut / FanIn + FanOut ``` # I 介於 0 ~ 1: 0 最穩定, 1 最不穩定 # FanIn 輸入依賴度: 外部有多少類別會依賴本身元件中的類別 # FanOut 輸出依賴度: 本身元件中有多少類別會依賴外部的類別 ``` * SAP 穩定抽象原則(stable abstracions principle) * 「抽象」: 則代表的是 只表達「概念」但並不「實作」 * 重要的元件, 應該越只表達純粹的「標的(一種概念可已是 需求、目標、行為、分類/歸類)」 * 核心原則即「隱藏細節」與「抽出標的(可已是 需求、目標、行為)。**舉個例子:** * 設計排程任務,每個排程任務的流程與細節都不同。如果針對「流程」與「細節」去抽象化,那這個抽象只能局限於**某個**排程使用。 * 進一步去思考會想到「彈性」、「擴充」、「可讀性」 * 那對於排程任務這個情境,可以抽出行為的特徵比如像是「初始設定」、「執行任務」... 這類標的。 * 抽出上述標的後可讓抽象變得較為彈性,以達到彈性與多型的需求。 * 抽象化沒有絕對的對與錯,只有較佳或較適的可行方案。 * 抽象是設計核心元件的重要指標 * 抽象是「標的」, 它沒有一個特定的表徵. 舉個例子 => ``` EX => 餐廳與廚師 # 由廚師本身會煮什麼菜, 來決定餐廳會有什麼樣的菜單. 但對於一定規模的餐聽, 可能不是一個好的情況 # 餐廳改成用 菜單 的概念來決定廚師要做哪些事情 # 就是將表達「概念」的「菜單」, 當成是最穩定的核心元件 ``` * 一般程式架構以常見的依賴關係由核心往外層可分為:```infrastructure => domain layer (商業邏輯) => Application layer => User Interface``` * domain layer * 通常就是系統的核心, 所以 Domain 的 Service 通常會宣告成 Interface 的介面 * 創建新的類別實作(implements) 該 Service 的介面 * 不應直接做資料存取, 比如調用 DAO(指 repository) 來完成資料保存的任務 <= 新觀念 * 借鑒了「SAP 穩定抽象原則」, 越穩定的元件應該越抽象 * Application layer * 負責流程作業, 相比於 Domain 變動性是比較大的, 雖然同樣有 Service 的類別, 但不創建 Interface 的介面 * 應該調用了 Domain layer 後, 再調用 DAO <= 新觀念 * 借鑒了「SDP 穩定依賴原則」把容易變動的東西, 變得更加容易改動 * Infrastructure => 通常放共用工具的地方 ``` SDP & SAP 兩個原則總結: #「穩定」與「不穩定」並不是一個「好」與「壞」的關係 # 最容易變動的「User Interface 使用者介面層」、「Application layer 應用層」, 就是放在上兩層, 向下依賴 # 越核心的通常要越少依賴, 且越抽象. !!主要明確行為與特徵. 在測試上也方便抽換!! ``` --- ### MVc 在各大軟體開發商推出的 frameworek, 會讓新手誤以為就是這樣 (觀念修正與思想衝擊) 文章: [MVC是一個巨大誤會](https://blog.turn.tw/?p=1539) [框架不應該有「MODELS」資料夾](https://blog.turn.tw/?p=1541) 整理兩篇文章的要點: 1. 現行 WEB 主流的 MVC 框架, 其實架構大多為 MODEL2 2. 雖不是 MVC 但 MODLE2 的學習成本很低, 符合商業模式 3. 不是小白的軟體工程師, 應該準備開始煩惱軟體架構這件事 反思: 1. 當用了現行別人設計好的框架時, 不小心會掉入舒適圈. (糖果有時是毒藥) 2. 想當年紅遍全台的 WEB FORM, 為什麼新專案已不見蹤影. (經歷過的就知道再說什麼) 3. 多想想與試著了解: 為什麼會誕生、簡單的概念、怎麼達成的、簡單的原理...