# 設計模式 ###### tags: `python` ### 推薦資源 圖靈星球 > ref: [Youtube](https://www.youtube.com/watch?v=lJiFZ7SLlGQ&list=PLV5qT67glKSFAhREF8DpbN6fn3VFSr8J7&index=49) Camp > ref: [Notion](https://vip.studycamp.tw/t/topic/6402) > ref: [Web](https://bright-yuzu-f3e.notion.site/53fae70b3fc649a8bd52cfe8a18d808e?v=00ee179b0b5a40219433b83b522e5347&p=82db858b7b4d422db04033bdcd093125&pm=s) > ref: [Youtube](https://www.youtube.com/watch?v=Urw1T7M3Ans&list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw-) Jet 丁哥 > ref: [Youtube](https://www.youtube.com/watch?v=paE7QzU10F0&list=PLTi9xTwWdTfLfbUbLgzzIm8GPjNJw-TAu&index=28) ArjanCodes > ref: [ArjanCodes](https://www.youtube.com/watch?v=jjoLejA4iAE&list=PLC0nd42SBTaNuP4iB4L6SJlMaHE71FG6N&index=92)) 何老師爬蟲 > ref: [Youtube]( https://www.youtube.com/watch?v=F25AH5_DYQw&list=PLqbNZvhUuEepTpfm1es_0PVD_FSmcclHE) 成為看起來很強的後端 > ref: [Youtube](https://www.youtube.com/watch?v=JU-ufoD7sZc&list=PLS5AiLcCHgNxd341NwuY9EOpVvY5Z8VOs&index=8) ### python設計模式框架(Framework) ![image](https://hackmd.io/_uploads/Sk4A2vSFT.png) >ref:[Python Design Patterns Brandon Rhodes](https://www.youtube.com/watch?v=Er5K_nR5lDQ) python的first class function特性讓以下黃色的pattern被淘汰 ![image](https://hackmd.io/_uploads/HyZggOrt6.png) >Ref: >https://www.youtube.com/watch?v=vVRp4OE9yOE&list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw-&index=13 >https://medium.com/@a4793706/design-patterns-python-%E4%BA%8C-%E7%8B%80%E6%85%8B%E6%A8%A1%E5%BC%8F-11341b33f047 >https://www.youtube.com/watch?v=vVRp4OE9yOE&list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw-&index=16 >https://www.youtube.com/watch?v=vzTrLpxPF54&list=PLC0nd42SBTaNuP4iB4L6SJlMaHE71FG6N&index=3 https://www.youtube.com/watch?v=FafNcoBvVQo&list=PLlsmxlJgn1HJpa28yHzkBmUY-Ty71ZUGc&index=16 https://www.youtube.com/watch?v=tJ8iFbRsOJ8 https://www.youtube.com/watch?v=RidaxQqB0wE&list=PLV5qT67glKSFAhREF8DpbN6fn3VFSr8J7&index=26 https://blog.csdn.net/u012839256/category_9956530.html ![image](https://hackmd.io/_uploads/B1iDjsBvT.png) ![image](https://hackmd.io/_uploads/BJsScoSPp.png) ![image](https://hackmd.io/_uploads/r1m-P_ewT.png) > Ref: > https://www.youtube.com/watch?v=D3YZUvV-sVA > https://www.youtube.com/playlist?list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw- > https://vip.studycamp.tw/t/topic/6450 ![image](https://hackmd.io/_uploads/Hy-tjVjUa.png) java dependency injection 範例 https://www.youtube.com/watch?v=J1f5b4vcxCQ ## 引言 >ref: https://waterballsa.tw/design-pattern ### 看透軟體的本質做出有效決策 軟體 =(行為+結構)→ 需求 式子的兩端:「軟體」和「需求」都是無形的,而這兩者卻定義了在設計程式時的情境 (Context)。若我們不用力釐清 Context,那所有「設計決策」都是空虛且強烈主觀的,導致我們無法自信地講出設計決策中的層層根據,也缺乏有效的思路來討論和佐證每道設計元素的必要性。 ### 無形變有形:OOA結構化分析 收到需求後,接著做物件導向結構化分析。透過「單句句構分析」和「區分結構與行為」兩個關鍵動作,來在短時間內,一但將需求閱讀完畢,便分析出了需求涉及的「領域模型 (Domain Model)」。 ### 精準定義設計情境 對焦需求,從功能性需求和非功能性需求中,察覺Forces,並善用模式語言的思維來精準地定義情境(Well-Defined Context)。在領域模型的結構上,去梳理設計結構「失去生命力」的各組約束條件。 ### 精準套用設計模式 熟悉所學的設計模式,快速從 Well-Defined Context 和其中的 Forces 來推理對應的設計模式。做出有憑有據的設計決策,說服所有人。並在套用設計模式的過程中,善用「依賴反轉之重構三步驟」來精準萃取出抽象,以避免模式在套用上失準。 ### 精準物件導向程式設計 參考設計藍圖,以及其中所得到的一版「有生命力」的結構。分工下去做物件導向程式設計,來獲得進一步的設計資訊。在程式設計中繼續察覺 Forces,觀察是否有遺漏的設計難題需要解決。 # 新想法: 1. 寧願代碼重複,也不要隨意使用抽象,因為更改代碼時的痛苦:使用抽象的code >過度耦合的code 2. 僅在有明顯好處時,才使用抽象。例如: - Many implementations with complex construction - Deffered execution from creation > https://www.youtube.com/watch?v=rQlMtztiAoA 3. 創建不會同時有多個實現的介面是一種浪費。除非是為了程式碼與 API 或資料庫等外部系統通訊。僅僅為了一個假設性的擴展而創建一個抽象類別並不是一個好方法。 4. 程式碼重複(就技術債而言)比錯誤的抽像要便宜得多 5. 「重複相同的邏輯」和「當前巧合地具有相同的邏輯」之間是有區別的。發現差異可能很困難,但如果消除後者的明顯重複,您會發現程式碼的可塑性變得較差。 6. 如果您對您的問題或未來的需求沒有完整的了解,請不要抽象化 7. 思考你擁有的數據以及你需要對這些數據做什麼比弄清楚抽象更重要。找出執行工作實際需要哪些數據,並編寫程式碼來操作該結構。這是面向數據設計背後的主要思想 8. 我喜歡將我的程式碼視為“基礎設施”和“功能”。基礎設施是邏輯,例如命令執行程式、頁面處理或範例中的遊戲引擎的基類。功能是您的命令、頁面或遊戲中的不同精靈或關卡。基礎設施是抽象存在的地方。功能(幾乎)從不共享代碼。基礎設施大約佔程式碼庫的 5% 或更少。 # 基本名詞: 1. 抽像是從多個類別中提取通用功能以創建更通用的實現的過程。 2. # 新設計模式: 1. Creator 創建者原則:決定在哪裡創建object,例如增加訂單的方法,應該歸類在銷售類別而不是產品類別 2. Information expert 訊息專家原則:決定在哪裡分配新的職責 3. Controller 控制器原則 4. Protected variations 5. Indirection # :memo: 設計模式的種類 ![](https://hackmd.io/_uploads/HJygssuzT.png) ## :memo: 了解為何需要設計模式? 設計模式為了解決什麼? ### 需求描述 As a 產品經理 I want 多國語系介面 So that 顯示使用者對應的語系內容。 ![](https://i.imgur.com/4UGsvOA.png) ### 類圖: - 畫出所有的Use case, 並嘗試遍歷所有case, 描述這個case 做什麼事情 - 例如:接收外部指令:接收指令是為了派發給XXX - 針對這個描述,在已有的core object內填充所需要的信息 ### keyword: - pattern: 情境、問題、解法 三者之間的關係 - 情境!=意圖 (無我) - 思路: 富有進展和根據的通用思考過程 - 軟體 = 行為+結構 -> 滿足功能性需求 - 藉由重構滿足非功能性需求:時限、預算、效能、維護性、擴充性、可觀察性、容錯性、可測試性、開發體驗、資訊安全 - 步驟: - step1: 看清楚context - 收到需求,把需求帶到軟體的過程 可視化/有形化/結構化 - 用物件導向分析(object oriented analysis, OOA)分析結構與行為, - step2: 才能察覺forces - ex. 未來會擴充的功能、資安、效能、跨平台、技術套件的更動、滿足領域中的商業邏輯、團隊開發能妥善分工 - step3: 才能評估solution - 查表,找出解決force時的常用pattern - step4: 才能發展pattern - 明白形式後,套用模式,有自信的解決問題,然後進入新的情境 - step5: 並且在套用Pattern後進入另一個context - 精通設計模式就是在精通這五項認知中的連貫性 - 連貫後速度就快、出手就準 ### 學習設計模式的好處: - 提供軟體設計的客觀度量 - 再利用的解決方案 - 確立的通用術語 - 使用物件導向技巧來提升軟體品質 - 提升 - 可讀性 - 維護性 - 擴充性 > ref: https://www.youtube.com/watch?v=ovmx5ZGcDP0 ### 程式碼的壞味道 1. 使用switch來分支不同的方法,缺點:每次擴充方法都要回到原本的類別做修改 2. 使用多型來分支不同的實作,缺點:孫類繼承子類最後會導致則組合爆炸 (濫用繼承的結果) 3. ### 觀察重點: - 業務主要要達成什麼目的 - 主程式如何呼叫類 - 需要至少知道哪些類的屬性和方法 - 哪些類的屬性和方法不需知道 - 誰決定了實例化時的參數值 - 哪個類在類中實例化了其他類 - 哪個類的屬性接收了其他類的實例 - - design pattern 的名稱及分類 - 意圖(目的/預期成效) - 動機(解決何種問題) - 適用場景 applicability - 物件導向結構圖(類別圖) - 物件實體參與者說明(Participants) - 參與者之間的互動說明(Collaborations) - 套用模式後的優缺點(consequences) - 如何實作此模式(Implementaions) - 行為及責任 - 爆炸 - 單一職責原則 - 關注點分離 ### 依賴注入 DI vs. 控制反轉 IoC vs.依賴反轉原則 DIP > ref: 詳細介紹: https://hackmd.io/@kenny88881234/rJwqJbT4S 依賴注入 ( Dependency Injection ) : 為一種設計模式,將依賴通過注入的方式提供給需要的 class,是 IoC 與 DIP 的具體表現 控制反轉 ( Inversion of Control ) : 為一種思想,函式主要功能之外的其他功能應該由第三方完成 依賴反轉原則 ( Dependency-Inversion Principle ) : 為一種思想,較高層次的模組不應該依賴較低層次的被模組,應該提供接口讓較低層次的模組實現 目的都是為了解偶,使程式好維護,且更容易測試 - 控制反转是框架和库的关键区别所在。对于一个库而言,用户程序员使用的方式是主动调用它,这是通常情况的做法,也就是“正向”控制;而对于一个框架,往往将用户程序员编写的代码注册到框架中,最后由框架来调用用户程序员编写的代码,这就构成了控制反转。也就是说,控制反转的关键在于“控制者”是谁。对于一个库而言,复用的可能只是算法和数据结构;而对于一个框架而言,复用的往往还有控制流逻辑,这也是控制反转的结果。 ### 設計模式slide: > Ref: Youtube 水球潘 ![](https://i.imgur.com/v6iBF9K.png) ![](https://i.imgur.com/grNPN07.png) ![](https://i.imgur.com/qghmw8a.png) ![](https://i.imgur.com/QLUC4SH.png) ![](https://i.imgur.com/3ub2BJy.png) ![](https://i.imgur.com/RzrqLuN.png) ![](https://i.imgur.com/YFjiEIn.png) ![](https://i.imgur.com/xOzU9ku.png) ![](https://i.imgur.com/v6FApBp.png) ![](https://i.imgur.com/hr4KdoI.png) ![](https://i.imgur.com/REALVtv.png) ![](https://i.imgur.com/dNX4WCs.png) ![](https://i.imgur.com/PPaUgDX.png) ![](https://i.imgur.com/hyerTUo.png) ![](https://i.imgur.com/BSVXLmT.png) ![](https://i.imgur.com/K57ZmuZ.png) ![](https://i.imgur.com/GPnikkY.png) ![](https://i.imgur.com/OwVuHgM.png) ![](https://i.imgur.com/JVsn1W0.png) - 先做OOA (object oriented analysis) - 再做OOD (design) ![](https://i.imgur.com/TOliVIg.png) ![](https://i.imgur.com/OrxpXBD.png) ![](https://i.imgur.com/S6SsO0k.png) ![](https://i.imgur.com/AyNjni4.png) - 看起來還行,直到新需求進來 ![](https://i.imgur.com/A19ftVq.png) ![](https://i.imgur.com/v3VFzX1.png) ![](https://i.imgur.com/szi2rog.png) 責任鍊模式 ![](https://i.imgur.com/K7Bza4x.png) ![](https://i.imgur.com/tiYJiOH.png) ![](https://i.imgur.com/39JI4Ls.png) ![](https://i.imgur.com/0tF4iMp.png) ![](https://i.imgur.com/4ZtHEG2.png) ![](https://i.imgur.com/RJvrm3d.png) ![](https://i.imgur.com/WFzGkQy.png) ![](https://i.imgur.com/NYiXqXP.png) ![](https://i.imgur.com/K0DYwIn.png) ![](https://i.imgur.com/piR6LWt.png) ![](https://i.imgur.com/UeFMsUX.png) ![](https://i.imgur.com/r2WQfPj.png) ![](https://i.imgur.com/MtRpZNu.png) ![](https://i.imgur.com/pmI1OnB.png) ![](https://i.imgur.com/p2plOdm.png) ![](https://i.imgur.com/gfTUocC.png) ![](https://i.imgur.com/NuIMFaE.png) ![](https://i.imgur.com/J44mZh1.png) ![](https://i.imgur.com/tM44axm.png) ![](https://i.imgur.com/YCFyJKx.png) ![](https://i.imgur.com/BdLpS7w.png) ![](https://i.imgur.com/0C6UURl.png) ![](https://i.imgur.com/cyVI8YU.png) ![](https://i.imgur.com/FhaHNK8.png) ![](https://i.imgur.com/FKg1WfX.png) ![](https://i.imgur.com/ZmiLhgL.png) C1M1S2:這個世界——正是物件導向的! C1M1S3:物件 (Object) vs. 類別 (Class) 「是先捕捉物件才分出類別」 C1M1S4: 類別的關係 (Relationship) C1M1S5:我做一次,你做一次!一起建出領域模型吧! C1M1H1 ★: Showdown ! 撲克牌遊戲建模 C1M2S1: OOP|讓水球軟體學院動起來! C1M1H2 ★: Showdown ! 讓撲克牌遊戲動起來! 進入 OOD|設計模式:掌握行為型模式C2M0S1: 軟體設計模式「因為釋放了 Forces,所以你重獲了生命力」 抽象類別 (Abstract Class) vs. 介面 (Interface) —— 了解何謂「萃取」 (Abstract) 萃取出抽象的標準流程 C2M1S1: 策略模式「行為是會變的!」 C2M1H1★:策略模式 ——你的配對策略是什麼呢? C2M2:樣板方法 「只有一部分的行為會變啦!」 C2M2H1 ★★:樣板方法——寫一個牌類遊戲框架吧! C2M3: 責任鏈模式「實作 Discord Bot 機器人!」 C3M3A:開閉原則「最大的擴充性」 C2M3H1★:責任鏈模式——碰撞偵測 BOSS 戰 ★★:大老二撲克牌遊戲魔王題 設計模式:掌握進階型模式C3M1:觀察者模式「響應式行為」 C3M1H1★:觀察者模式——Youtube C3M1A:單一職責原則「One reason to Change」 C3M1B:里氏替換原則「繼承我的意志」 C3M2:指令模式 「執行!」 C3M2H1★:指令模式——快捷鍵設置 C3M3:狀態模式「狀態機圖 (State Machine Diagram)」 C3M3H1 ★★:狀態模式——寶藏地圖 BOSS 戰 ★★★:行為變動性多到不行!RPG 終極之戰!魔王題 掌握結構型模式C4M1:門面模式「體驗的門面」 C4M1H1★★:門面模式——處方診斷系統 C4M2:轉接器模式「不被介面牽著鼻子走」 C4M2H1★:轉接器模式——好友關係分析器 C3M2A:依賴反轉原則「 從意義衍生出意圖,再衍生出第二層次的意圖」 C4M3:代理人模式「代我出席啦!」 C4M3H1★:代理人模式——員工資料表存取 C4M4:複合模式「透明的樹!」 C4M4H1★★:複合模式——日誌框架 C4M4A:介面隔離原則「最小認知」 C4M4:裝飾者模式「組合爆炸的行為」 C4M5H1★:裝飾者模式——服務探索 (Service Discovery) 機制 BOSS 戰★★:IoC 控制反轉框架魔王題 掌握創建型模式C5M1:單體模式「Always ONCE」 C5M1H1★:單體模式 C5M2:工廠方法「唾手可得!」 C5M2H1★:工廠方法 C5M3:抽象工廠模式「Service Provider」 C5M3H1 ★:抽象工廠——可抽換式元件架構 (Pluggable Architecture) C5M4:建築者模式 「同樣的過程,全然不同的結果」 C5M4H1 ★:建築者模式——Model-View-Presenter (MVP) 模式 BOSS 戰 ★★:創建型模式魔王題魔王題 自幹一個 Web Framework物件導向分析:框架類別圖 物件導向設計:一路重構化解 Forces 物件導向程式設計:快速寫出 Web Framework 終極 BOSS 戰 ★★★★:用自己寫的 Web Framework 寫一個 RESTFul Web ### 何謂設計模式: - 某個情境下,常見問題的解法 - ### 程式碼風格: 追求: - 易閱讀 - 易維護 - 易擴充 ### 如何辨別此方法是否要放在類別內? ![](https://hackmd.io/_uploads/r1tHyeyZT.png) ![](https://hackmd.io/_uploads/BkjUelkZp.png) > ref: https://youtu.be/FPPIdd5--EY?list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw- ### SOLID 設計原則: - ![](https://i.imgur.com/kbADinb.png) > https://www.youtube.com/watch?v=FPPIdd5--EY&list=PLxHlVV5gCbcNFOWAwtGGG-h8lL_Hx7pw- - SOLID 設計原則 - SRP: 單一職責原則 (single responsibility) - 高內聚(避免大而全,避免不相關的耦合)、低耦合(減少所需要依賴和被依賴的類) - 一個組件會變更的原因(即職責),應該來自同一個業務關聯方(同一群會對組件提出需求的人),亦即決定組件之「職責」為何的是人,而非其他組件或系統 - 遵循SRP後,單一組件需要變更的業務原因將單存化,因為業務耦合遭成的維護問題將大大減少 - 當組件規模增加時,組件能力和業務關聯方可能增加,此時需要開始透過SRP將屬於不同業務關聯方的組件進行拆分、業務單純化。這是一個持續迭代的過程 - ![](https://i.imgur.com/tORG0TB.png) - ![](https://i.imgur.com/4tVZTpM.png) - ![](https://i.imgur.com/qpdLbOT.png) - ![](https://i.imgur.com/zj9H6tN.png) - ![](https://i.imgur.com/QVl2Y08.png) - ![](https://i.imgur.com/PKp7ZrV.png) - 一個模組應該只對唯一的一個角色負責 - 若不同角色共用模組,建議將模組拆開避免更改後互相影響 - ps. 模組指原始檔or類別 - ps. 角色指特定群體使用者(ex.業務部,行銷部) - conway定律:軟體架構和團隊組織結構是互相影響的 - 利用領域驅動設計概念,將職責單一化 - 基礎設施層負責系統 - 領域層負責角色 - 應用層負責流程 - 用戶介面層 - OCP: 開放封閉原則 (open/closed principle) - 一個軟體應該對擴展是開放的,但對於修改是封閉 - 好處:測試簡單、可復用性強、穩定性高 - 實現方式:接口和抽象類 - ![](https://i.imgur.com/PjXfrS7.png) - ![](https://i.imgur.com/jyBU62x.png) - ![](https://i.imgur.com/EEzt0pZ.png) - ![](https://i.imgur.com/hVs1ucu.png) - ![](https://i.imgur.com/OY6VuRj.png) - ![](https://i.imgur.com/V7s46i9.png) - ![](https://i.imgur.com/55oQx5F.jpg) - ![](https://i.imgur.com/eZOhUKV.png) - ![](https://i.imgur.com/AzTLdFJ.png) - ![](https://i.imgur.com/3LOR7nf.png) - ![](https://i.imgur.com/0k3zG98.png) - ![](https://i.imgur.com/uVFCYBn.png) - ![](https://i.imgur.com/xdall36.png) - LSP: 里氏替換原則 (Liskov substitution) - 程式中的父類應該是可以在不改變程式正確性的前提下被它的子類所替換 - 原則: - 保證基類所擁有的性質在子類中依然成立 - 子類拓展父類的功能,但是不能改變父類原有的功能 - 如何實現: - 子類必須完全實現父類的抽象方法,但不能覆蓋父類的非抽象方法 - 子類可以實現自己特有的方法 - 當子類可以實現父類的抽象方法時,方法的後置條件要比父類更嚴格 - 子類的實例可以代替任何父類的實例,但反之不成立 - ![](https://i.imgur.com/eFEqYQK.png) - ![](https://i.imgur.com/bRdpIUH.png) - ![](https://i.imgur.com/0ra8HSm.png) - - ![](https://i.imgur.com/o7ukl04.png) - ![](https://i.imgur.com/1cqFyaG.png) - ![](https://i.imgur.com/bGCbYwl.png) - ![](https://i.imgur.com/X63UWsj.png) - ![](https://i.imgur.com/SgCwkYF.png) - ![](https://i.imgur.com/QIzbHgR.png) - ![](https://i.imgur.com/qqZpZvL.jpg) - ![](https://i.imgur.com/VUS93Gc.png) - ![](https://i.imgur.com/Y0IM2mt.png) - ISP: 介面隔離原則 (Interface Segregation) - 多個特定客戶端介面要好於一個廣泛用途的介面 - 客戶端不應該依賴不需要的接口 - 類之間的依賴應該建立在最小的接口上面 - 接口隔離:多個轉門的接口 好於 單一的總接口 - 約束偏好:接口隔離原則偏向約束架構設計,單一職責原則偏向業務邏輯 - 細化程度:單一職責更精細,接口隔離注重相似接口的隔離 - ![](https://i.imgur.com/XwhLz8y.png) - ![](https://i.imgur.com/rXOc0Sv.png) - ![](https://i.imgur.com/84gJZFN.png) - ![](https://i.imgur.com/ReMhCot.png) - ![](https://i.imgur.com/lnxNVtv.png) - ![](https://i.imgur.com/6Hwnl7h.png) - ![](https://i.imgur.com/QBz0gKv.png) - ![](https://i.imgur.com/Mb09a3H.png) - ![](https://i.imgur.com/PfSZ4o0.png) - ![](https://i.imgur.com/rpvoZOo.png) - DIP: 依賴反向原則 - 高階不要依賴低階,兩者應該依賴於抽象 - 抽象不應該依賴於細節,細節依賴於抽象 - 範例:設計模式中的工廠方法 - ![](https://i.imgur.com/OoEebUm.png) - ![](https://i.imgur.com/AaijFWT.png) - ![](https://i.imgur.com/DgP0MDg.png) - ![](https://i.imgur.com/bkATexw.jpg) - ![](https://i.imgur.com/EAUnLHL.jpg) - ![](https://i.imgur.com/cVQI4V9.png) - ![](https://i.imgur.com/14D2Xl3.png) - ![](https://i.imgur.com/bv5PEA1.png) - ![](https://i.imgur.com/hMzJczO.png) - ![](https://i.imgur.com/Eqo91I1.png) - ![](https://i.imgur.com/Lf9r8Kt.png) - ![](https://i.imgur.com/b9yD47M.png) - ![](https://i.imgur.com/B9yo85g.png) - ![](https://i.imgur.com/25QdtAx.png) - ![](https://i.imgur.com/jPb0jX5.png) - ![](https://i.imgur.com/eetWTz8.png) - 總結: - ![](https://i.imgur.com/E2qu6qd.png) - ![](https://i.imgur.com/3oO3fJ9.png) - ![](https://i.imgur.com/TbXk6JO.png) - ![](https://i.imgur.com/0x6oLPq.jpg) - ![](https://i.imgur.com/Zhs1G4z.png) - ![](https://i.imgur.com/xYBrMlj.png) - ![](https://i.imgur.com/01tjN5q.png) - 迪米特法則(最少知識原則) - 一個類對其他類知道的越少越好 - 只與朋友做通信,朋友:有耦合(依賴、關聯、組合、聚合)關係的對象 - 直接的朋友:成員變量、方法參數、方法返回值、方法返回值中的類 - 陌生的類最好不要當作局部變量出現在類的內部 - 如果要取得其他類的變量,可以由第三者轉發這個調用 - 好處:降低耦合性 - 不要做以下行為 - objA.getobjB().doSomething() - 遵循迪米特法則的設計模式 - 外觀模式Facade - 中介模式Mediator - 注意事項:有可能存在大量的中介類 - 合成復用原則(composite/aggregate reuse principle, CARP) - 內容:盡量使用聚合/組合,而不是繼承來達到軟件復用的目的 - 聚合vs組合vs繼承 - 聚合: has A - 組合: contains A - 繼承: is A - ![](https://i.imgur.com/d011L5M.png) - ![](https://i.imgur.com/xzYDGbG.png) ### 其他principle ![](https://i.imgur.com/mVYHuPu.png) >ref: https://www.youtube.com/watch?v=cSQY0rA7FPU - 領域設計:由上到下:用戶介面層>應用層>領域層>基礎設施層 - 領域層可以抽象化,方便擴充 - 領域層不應該直接調用基礎設施層(ex. DAO, data acess object ; util第三方功能) - 應該由應用層來完成任務。避免DAO的更動影響到領域層的功能 - 或者可在領域層建立抽象類別,再交由基礎設施層實作 - 領域層是核心元件,不應該依賴其他元件 ### SOLID 在python中的寫法 - 用from abc import ABC 建立抽象類 - 不同功能的class 可以繼承同一個抽象類, 要擴充時只要新增新的class, 而不會違反開放封閉原則 - 若有某幾個類的參數要和繼承的抽象類不同,可以把不同的參數寫進去__init__內,而不會違反里氏替換原則 - 為了讓class的參數不要直接使用實例,可以讓class參數直接使用抽象類,來達到依賴反轉原則 > ref: https://www.youtube.com/watch?v=pTB30aXS77U # ### write BETTER PYTHON CODE - 代碼重構 - 用dictionary取代if else - class registor 內可以有 def add_object - 可以先定義 class car, 再由另一個class的method return car(x1,x2,x3) - 最後再用class application 讓使用者輸入應該輸入的部分即可 (資料層、業務層、應用層 分開) > ref: https://www.youtube.com/watch?v=eiDyK_ofPPM - 依賴反轉原則的寫法 > ref: https://www.youtube.com/watch?v=Kv5jhbSkqLE 利用ABC定義abstract class,讓子類繼承,定義了子類的方法名稱。故要替換子類時,原本的code仍可執行(因為另外一個子類也有相同的方法名) 反例:LightBulb和ElectricPowerSwitch強依賴(需要lightBulb object 且直接調用其方法) ![image](https://hackmd.io/_uploads/Sy_Sh4yIT.png) 建議:利用依賴反轉原則,解除LightBulb和ElectricPowerSwitch之間的依賴 ![image](https://hackmd.io/_uploads/rJYf1HkU6.png) ![image](https://hackmd.io/_uploads/ryOY1ByLT.png) - Interpreter 解譯器模式 (難用) - 使用時機:Interpreter並非單單用在語句或語言的轉換上,而是用在任何可以組合和重複利用的邏輯 - 創建context類,包含input和output屬性,其中output屬性為newdata類的實例 - 創建多個interpreter子類(繼承抽象父類) - 主程式在呼叫interpreter實例中的方法,對context.ouput中的屬性進行修改 > ref: https://ithelp.ithome.com.tw/articles/10193177 ### 未歸類: - 隔離不可控之物 ![image](https://i.imgur.com/bT2qEJH.png) ### 類的應用: - 一個實例A的屬性可以是另外一個實例B - 或一個實例A的屬性為列表,列表中包含實例B,C,D... - 好處: 實例A可以直接訪問實例B的屬性與方法 - 在A類中建立一個方法,此方法可以設定A.屬性=B實例 - 此時我們可以說:A依賴於B - 範例:Member().get_car().get_info() - 一個實例A的屬性如果是列表,可以使用列表的方法 - member.get_children().append(chd_a) - 利用多態的特性,可以把實例當作參數傳遞給def,並在def內呼叫該實例的方法 - ex. def check(obj): obj.getinfo() - __new__()比__init__()優先執行,且可控制是否要執行__init__ - 覆寫__str__可改變print(實例)的輸出 、定義__repr__則不會影響print(實例)的輸出 (此功能主要給開發者在調適模式用的) - __eq__ 定義看兩個instance 是否相同的判斷依準 - 應用: list[實例1,實例2].remove(實例1) - __bool__ 直接呼叫instance 返回Ture or False - 應用:if 實例1 - __format__ 對instance格式化 > ref:https://www.youtube.com/watch?v=qoovdSthXso&list=PL2BYd135G1o9r-TAMw-QCddLnJFpOXpRY&index=134 - __call__ 可以用實例()來調用__call__的內容 - __import__ 可以取代import語句 - __getattribute__ 每次調用實例.屬性時會呼叫 - __dict__ 以字典形式的output實例屬性 - __setattr__() 設定實例屬性前會呼叫此方法,需額外設定self.__dict__[key]=value - __getattr__() 獲取實例屬性時,若該屬性不存在會呼叫,需額外設定self.__dict__.pop(item) - __delattr__() - __init_subclass__ 定義在父類中,當子類實例化時,便會被調用,用以獲取子類實例化的訊息 - __next__、__iter__ 若要讓實例可迭代,則須設定 > ref: https://www.youtube.com/watch?v=Zje5Rfz4UP0&list=PL2BYd135G1o9r-TAMw-QCddLnJFpOXpRY&index=141 - __reversed__ 可以reverse實例 - __setitem__、__getitem__、__delitem__ 可以設定實例的字典 ### 其他知識: #### 多繼承 - 多繼承時,先訪問左邊的父類,若左邊沒有才訪問右邊,都沒有才訪問爺類 - 實例名稱.__class__ 可得知是從哪一個類實例化的 - 父類可以透過__subclasses__()獲得子類列表 - 子類可以透過__base__獲得父類 - issubclass(A,B) - isinstance(obj,class) #### interface vs abstract class interface & abstract class 兩者共同之處都是抽象層,包含了抽象的方法,而且不能直接生成 instance,然而 Abstract class 比較是層次上的抽象 (根源的繼承關係),而 interface 像是對行為動作的抽象。 - Abstract class 是要**創建一個能夠體現某些行為的類別**,例如哺乳類動物有哪些行為,設計理念上表現的是 "is-a" 的概念。Abstract class 也可以包含已經有實作的方法來讓子類別去重複使用。 - interface通常只有抽象的方法,設計上則是表示 "has-a" 的概念。interface 比較像是**對類別的行為有約束**,可以讓不同的類別去擁有相同的行為。例如"飛",而很多類動物例如哺乳類或鳥類都可能可以飛,就會用不同方式去實作 "飛"。 - 通常一個 Class 可以實作多個 interface (不同於繼承通常都只能單繼承)。 - 如果你的設計哲學是例如 A 是不是 B 的一種,如果是,那麼 A 就能做 B 能做的事。如果是這樣的話就考慮 Abstract class 和繼承。 - 如果你的設計哲學是關注在動作上,例如 A 如果可以擁有某些動作我們可以把 A 拿來使用,那就用 interface,而不是關注在於說 A 是不是一定要是某一類。 - Abstract class 的代價比較高,因為通常只能單一繼承,也比較不容易去設計,因為必須把子類別中共同的屬性和方法抽象出來。 - 但 interface 只要把一些特定行為抽象出來變成一個 interface,而每個類別又可以聲明實作了多個 interface,在設計上會比 abstract 來得更有彈性跟擴充性。 > ref: https://ithelp.ithome.com.tw/articles/10223079 #### 什麼是metaclass 我們可以理解為是 class 的 class,因為在 Python,什麼東西都是 object,包含 class 本身也是一個 object,那麼既然是 object,一定會有一個像是 class 的東西來產生這個 object,而這個 class 的 class 就是 metaclass 啦!而一般的 class 預設的 metaclass 是 type。當我們去宣告了一個 class 例如 A ,而要用 A 去產生 object 的時候,其實是會先 call type 的 __call__ Method,而 __call__ 的行為是去 call A 的 __new__ 和 __init__。所以假使我們今天改變了本來預設的 metaclass,就能夠改變物件生成的行為。例如常見會去使用 metaclass 的一種情境是 Singleton object,就是利用自定義 metaclass 來避免每次都去產生一個新的 object, #### 什麼是duck typing DuckTyping,是動態型別語言的一種風格,最常拿來解釋的一句話就是"當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子"。(不在乎是否是真的鴨子,而在乎屬性和方法的集合是否能達到鴨子的效果) - 在 DuckTyping,一個 Object 有效的屬性和方法,不是繼承自特定的類型或是實現了某個 Interface,而是看當下其所有的屬性和方法的集合。 - 對於 Python 來說,因為有多重繼承的關係,所以並不需要像 Java 另外引入 Interface 來達到類似多重繼承的目的。此外,因為 Python 是 DuckTyping,所以有些需要 Interface 的場景在 Python 也不需要。 - DuckTyping 的函式接收的是任意類型的 Object,而在函式呼叫該 Object 的特定方法,因此我們關注的是,這個 Object 是否可以執行這個行為,而不是這個 Object 的類型。好處是很彈性,壞處是程式必須要到了執行期才可能發現問題。 #### 組合 vs 繼承 繼承和組合,前者是 is a,後者則是 has a 的關係。一般來說,使用組合的比起繼承更加彈性以及不易有 Bug。但這並不表示繼承不好,而是說很多時候使用組合的方式可能會是更好的做法。當我們如果是使用繼承的話,必須要對父類別的實作去了解,這樣才知道我們需不需要在子類別去重寫,屬於 Whitebox Reuse。然而組合則是 Blackbox Reuse,表示不需要去了解 Object 的實作,而是把 Object 當作是 Blackbox 的方式去操作,只要我們替換了 Object 就能夠改變實際的行為方式。 - 繼承:定義一個汽車類別 (Car),並定義 accelerate 這個方法,而不同品牌的車 (CarA, CarB ...)則分別宣告子類別並繼承 Car ,並將 accelerate 做 Override。 - 組合:定義一個 Inrerface,其定義了加速的行為,例如 AccelerateBehavior,而不同的加速方式則是定義不同的類別去實作這個 Interface,於是我們不用定義 CarA 或 CarB,我們只要有一個類別 Car,而 Car 有個屬性 accelerateBehavior 類型是 AccelerateBehavior ,我們可以在創造 Car 的時候給予這個屬性不同的實作類別 (例如 AccelerateMethodA 或 AccelerateMethodB) - 因此根本來說,我們關注的是用繼承重寫還是給予不同的行為實例。 - 繼承的優點是使用起來較為直覺,畢竟繼承是本來物件導向語言原生就有的做法,也很容易去做到程式碼的複用,只需要去 Override 需要更改的部分。但缺點是子類別跟父類別之間綁死,要是父類別改動了,子類別也必須改變。 - 組合的優點是,由於組合是透過 Interface 引用,所以在複用的過程之中還能夠維持原先的封裝性。另外也如同上面所述,可以在執行期間輕易做替換,只要該類型實現了 Interface,就可以被拿來使用。而透過組合,可以讓類別的層次不會增長到一個無法管理的境界,讓層次維持很小,而每個實現 Interface 的類別都是專注在完成某個行為,更能夠維持內聚,也降低耦合。而缺點就是在程式碼的複用上比起繼承來說會比較需要多寫一些程式,以及涉及到的類別可能會比較多。 #### 常見裝飾器用途 ref:https://realpython.com/primer-on-python-decorators/ ### 其他IT課程: ### 專有名詞: - 領域驅動設計(Domain-Driven Design, DDD) - ![](https://i.imgur.com/ha6D7d3.png) - ![](https://i.imgur.com/6mHKsoE.png) - 中台架構 - 目前的作法是以領域驅動設計(Domain-Driven Design, DDD)進行業務領域的切分與規劃,找出核心能力、通用領域、支撐(Support)平台,之後就在此中台架構之上,進行個別系統的分析/設計/開發,最終以微服務的形式發佈,希望能解構(Decoupling)過往龐大的前後台單體(Monolithic)架構。 - 只注重系統設計,不管系統分析,如此進行中大型系統的開發,幾乎註定要失敗。 - 使用案例圖(Use Case Diagram) - ![](https://i.imgur.com/B1kphzb.png) - 類別圖(Class Diagram) - ![](https://i.imgur.com/rUg0DjR.png) - 順序圖(Sequence Diagram) - ![](https://i.imgur.com/FtNjJee.png) - 物件關聯對映 ORM - 物件關聯對映(英語:Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式設計技術,用於實現物件導向程式語言裡不同類型系統的資料之間的轉換。從效果上說,它其實是建立了一個可在程式語言裡使用的「虛擬物件資料庫」。 ### 其他好文: - DDD與設計模式 > ref: https://ithelp.ithome.com.tw/users/20001976/ironman/4471 ### 常見功能需求: - 資料的增刪改查 - 資料處理的ETL(extract/transform/load) - 資料的load/save - [x] **Bold** - [ ] *Italic* :::info :bulb: **Hint:** :pushpin: **Remark:** ::: > Note ## BONUS: More cool ways to HackMD! - Code block with color and line numbers: ```python=3.8 import pandas as pd ```