# 設計模式 (design pattern)(用作品前都必須要看) 大話設計 https://github.com/3masterplus/book/blob/master/%E5%A4%A7%E8%AF%9D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F(%E5%B8%A6%E7%9B%AE%E5%BD%95%E5%AE%8C%E6%95%B4%E7%89%88).pdf 歐洲SOLID原則 https://www.youtube.com/watch?v=rtmFCcjEgEw 歐洲laravel https://www.youtube.com/watch?v=qpo5KG0vIyE&t=358s 我們在開發功能的時候,都是讓大功能(高階模組)調用各個小功能(低階模組)來實現目標,越高層的就越整體、越抽象、越接近目標;而越低階就越細節、越接近實作,關注點越小。而我們的思維通常是由大範圍往下到小實作,從整體目標逐漸拆解成各個步驟。 **function 將概念封裝,將變數區域化** https://www.youtube.com/watch?v=M7Xi1yO_s8E 簡單的了解(150元買的) https://www.youtube.com/watch?v=W7N4XnZQF78 **不要用繼承 用組合** https://www.thepolyglotdeveloper.com/2018/09/inheritance-composition-php-application/ ![](https://i.imgur.com/ZMf83oB.png) https://www.koderhq.com/tutorial/php/oop-composition/ https://www.youtube.com/watch?v=nnwD5Lwwqdo is-a 不好 要用 has-a https://antrash.pixnet.net/blog/post/80808873 design pattern的前輩們帶來的忠告:多用合成、少用繼承 就是用依賴注入的方式 用組合 不要用繼承 ## SOLID https://www.youtube.com/watch?v=UQqY3_6Epbg&list=PLZlA0Gpn_vH9kocFX7R7BAe_CvvOCO_p9 ![](https://i.imgur.com/pDEenSE.png) ### 單一原則大概都懂了 ### 開放封閉原則 (Open-Close Principle) https://igouist.github.io/post/2020/10/oo-11-open-closed-principle/ https://wadehuanglearning.blogspot.com/2019/12/blog-post_11.html 主要邏輯和附加邏輯,附加到主要邏輯 **心業務邏輯簡稱為「業務邏輯」。也就是說系統中有可能 20% 是業務邏輯,剩下的 80% 是圍繞著業務邏輯延伸出來的附加邏輯。 舉例來說,一個診所掛號系統一開始只有「掛號與叫號」功能。但若需要的話,也可以延伸出「叫號時發送簡訊提醒患者」功能。掛號系統的案例中業務邏輯是「掛號與叫號」;而「叫號時發送簡訊提醒患者」則是 隨著時間與新需求延伸出來的附加邏輯。** **面對需求,對程式碼的改動是透過增加新程式碼進行的,而不是更改現有的程式碼   (《大話設計模式》)** ### 封閉擴充 if switch這些 都可以用擴充原則 因為他是之後要擴充 都要進去if 之類的做修改 這樣就不是擴充了(但小型的不用 不要過度使用設計模式) https://igouist.github.io/post/2020/10/oo-11-open-closed-principle/ **凡是變化都有成本,** 其實你我都已經很習以為常了,就是**模組化**。 **實行** 在主要邏輯和附加邏輯之間,加入抽象層來解耦合 + 介面 使用外部注入來處理附加邏輯。除了不將附加邏輯寫在類別中,降低修改的機會以外,和介面的邏輯一致:你給什麼工具我就用什麼工具 有點像是package一樣 你可以對她擴充 你只會使用他 然後增加 但不會改動他 這樣一直擴充 就等於她一直被測試 所以增強他穩定性 例子 ![](https://i.imgur.com/kzQtP0o.jpg) 看起來沒問題 但當你要擴充呢 ![](https://i.imgur.com/awP7v5O.jpg) 所以應該這樣寫 ![](https://i.imgur.com/pmx0aNG.jpg) 但動作一樣 所以要介面 ![](https://i.imgur.com/31wuPfI.jpg) ![](https://i.imgur.com/wpYEUBb.jpg) 所以controller應該這樣寫 裡面是上面那樣這樣才是工廠模式 ![](https://i.imgur.com/AGvZ5oz.jpg) 簡單來說你new他然後做同樣的事情 所以你應該把new當成工廠 他每次做的都一樣 區分是type 所以每一種都是function 然後一樣動作用interface 這是工廠 外面再傳入type就好了 就是不更動的前提擴充 ### 里氏替換原則 (Liskov Substitution Principle) 替換 這是最對object的原則了 用注入之類的 is-a 跟 has-a https://igouist.github.io/post/2020/11/oo-12-liskov-substitution-principle/ **子類別替換父類別後,不需要改變,也不會發生任何錯誤或異常。** **我們該如何遵守里氏替換原則?** 用子類別實作出各式各樣不同的方法,藉此讓父類別的方法藉此達到延伸和多樣化的效果」如此我們的物件彼此之間才能保持彈性,擁有可替換可擴充的特性,進而達到 開放封閉原則 **然而,這個擴展不該是天馬行空隨便亂擴的,必須要有原則** 最首要的就是:至少父類別能做到的事情,子類別也要能做到 一個好的擴展方式,應該能滿足這些條件: * 要求不應該比父類別多 * 回饋不應該比父類別少 建議看文章上面的 **我們預期了這個函式或類別需要準備的輸入參數,也預期了應該要有的輸出結果。如果某一天替換了子類別,卻不是這麼一回事,就會發生很多意料外的錯誤** 就是父親有的他也要有,你可以想成是oredrByquery那個 複寫 但還是要有原本功能 但記得不要亂繼承 功能(行為)可以用has 真的需要在繼承 ### 介面隔離原則 https://igouist.github.io/post/2020/11/oo-13-interface-segregation-principle/ 簡單 就分細一點 **介面也要單一職責** **但js不一樣可以看影片** https://www.youtube.com/watch?v=JVWZR23B_iE&list=PLZlA0Gpn_vH9kocFX7R7BAe_CvvOCO_p9&index=4 ### 依賴反轉原則 https://igouist.github.io/post/2020/12/oo-14-dependency-inversion-principle/ 依賴 匯出報表」功能建立了一個「Excel 控制類別」的實例以建立檔案;或是「會員查詢」功能建立了一個「DB 連線」的實例來進入資料庫取得會員資料,諸如此類由A模組直接藉由B模組的實例來完成想要的動作,就是依賴。 依賴反轉的原理 中間要多一個 ![](https://i.imgur.com/q7Nb8Cy.png) store 用 stripe付款 但可能改用別的paypel之類的 那你就用中間寫一個 負責付款的他可以接受不同付款的功能 ![](https://i.imgur.com/pDEenSE.png) https://igouist.github.io/post/2020/12/oo-14-dependency-inversion-principle/ 高階模組不應該依賴於低階模組。兩者都應該依賴抽象 (建議看文章) **依賴反轉最基本的思維路線。我們並不是用低階模組的功能直接拼湊出高階模組,讓高階模組直接依賴低階模組然後受到影響;而是把關注點放在需要的功能上,用介面隔開實作,解開他們彼此之間的耦合,介面就是模組之間的抽象層。** 簡單來說 就是公司repository一樣 方法都寫好了抽象方法 你去做 然後要改全部都會一起改 ## 最少知識原則 五大原則中L位的第一候補:最少知識原則,也被稱作迪米特法則。 **不應該使用其他類別的方法所回傳的類別的方法。** Foo.GetBoo().BooDoSomeThing() 這種情況,我們不該去跟 Foo 要 Boo 回來然後使用 Boo 的方法,因為我們只認識 Foo,而不認識 Boo。 ## 設計模式—建造者模式 (Builder Design Pattern) https://medium.com/wenchin-rolls-around/%E8%A8%AD%E8%A8%88%E6%A8%A1%E5%BC%8F-%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F-builder-design-pattern-7c8eac7c9a7 ## 分散式架構 分散式架構是指多個電腦 不是單一電腦那樣 多個資料庫 不是單一資料庫 防止single point Of Failure的風險 **分散式架構一定要小心資料一致性** 三大分類 運算 服務分流 儲存 ## 設計模式必懂得 UML講解 組合 ![](https://i.imgur.com/rs6B4me.jpg) 依賴關係 ![](https://i.imgur.com/ZElEGJ2.jpg) ![](https://i.imgur.com/xwbvaiS.png) ![](https://i.imgur.com/6JI8Ql4.png) ## 希望設計出來有的特性 **Scalabulity** 可拓展性/可縮短性 **Reliabiltity** 可靠性 系統無故障執行的概率 **Availbility** 可用性 系統不中斷運行時間占實際運行時間的比例 ![](https://i.imgur.com/Vd1hEea.png) 可用不一定可靠 可靠一定可用 **Effciency** 高效率 * latancy 延遲 執行一個操作要花費的時間長度 * Throughput 吞吐量 以一個時間區間作為單位 單位時間內可以執行幾次操作 或運算的次數 Manageability 可管理性(面對開發人員) 讓一個系統方面管理,ci/cd那種 能不能快速追蹤bug 快速把infrastructure(iac)抽象化 主要是讓開發人員能更專注在邏輯的運算上 ## 必要技能 **前端 後端也要看** cache proxy cdn load Balancing DataBase DB master-slave message queue schema design CAP Therem partition & Sharding Replication & Redundant ![](https://i.imgur.com/80cQwCd.png) ### cdn 就是一個分離式的概念 這樣要取東西不會去很遠的地方 不會要回應很久 他比較常存放靜態的 圖片的 東西 ### cache 基本 ![](https://i.imgur.com/QcXHS0I.png) 在分散式系統很難 因為會有資料不同步情況 ### load Balaning 多台主機那樣 ![](https://i.imgur.com/CBJRr9G.png) 有不同演算法 ex round-robin 一二三依照下去 last Connected 看誰最有空 ip-Hash 每次經過同一個加密 所以以後都會給同一個主機 ## 如何思考 **第一步** 釐清系統需求 兩大重點 function requirements 上傳照片邏輯之類的 non-function requeirements 上傳照片不會遺失,播放影片不會lag **第二步** 關於系統流量 容器 網路頻寬的粗略計算 **第三步** 定義System interface RESF API or GraphSQL **第四步** 定義 Data Model | DB Schema 資料欄位那些 **第五步** High Level Dsign 因為有前面幾步驟 知道具體流量跟資料庫等規範 這邊就能知道機台要不要水平擴展 ![](https://i.imgur.com/Ct0ucIq.png) 這步驟有基本架構 **第六步** System Detailed Design 更深入的設計 **第七步** find trade Off and Try to Solve it 找到系統的缺點 然後說出自己的不足 ### 以 Netflix 為例做系統規劃 https://www.youtube.com/watch?v=W7N4XnZQF78 ## 設計模式分類 設計模式太多了 主要使用 * 單例 (singleton) * 註冊表 (registry) * 工廠 (factory) * 觀察者 (observer) * 依賴注入 (denpendency injection) ## 實際應用 你必須實際把設計模式應用在自己的作品中,經過大腦的慎密思考,當在維護自己的作品時,思考如何讓作品更棒時,設計模式就會不知不覺、悄悄地內化了。甚至不需要知道用的是什麼名字的設計模式,因為它已經是內在的一部分 ## 設計模式 - 單例模式 (Singleton) (ps 在 laravel叫 trait) https://ithelp.ithome.com.tw/articles/10203092 ## 內容 單例模式 定義:**只有一個實例**,而且自行實例化並向整個系統提供這個實例。 屬於創建模式, 這個模式涉及到一個單一的類別,他必須要創建自己的實例, 並且確保只有單一個對象被創建。 這個類別提供一個方法訪問其被創建的唯一一個對象。 **存取IO和資料庫等資源,這時候要考慮使用單例模式。** 有幾種方式可以實現單例模式 * 懶散(Lazy)模式(線程不安全) * 懶散模式(線程安全) * 積極模式 * 雙重鎖 (Double ChockLock) * 登記式(靜態內部類) * 枚舉 (enumeration) ## 單例模式 ## 理解区分:简单工厂模式,工厂方法模式,抽象工厂模式 https://zhuanlan.zhihu.com/p/343638837 ## 策略模式 vs 工厂模式 工厂模式是一种创造模式。战略模式是一种运营模式。换句话说,工厂模式用于创建特定类型的对象。策略模式用于以特定方式执行一个操作(或一组操作)。在经典示例中,工厂可能会创建不同类型的动物:狗,猫,老虎,而策略模式将执行特定的动作,例如,移动;使用“跑步”,“步行”或“移动”策略。 ## Strategy Pattern 策略模式(非常重要 常用) 情境: 同一個行為有數種方案可以選擇,使用者可以在執行期間再決定要用哪一個 秘訣: * 出現用switch case來切換行為的場合 * 將行為定義成介面,實做出同一體系的類別 * 在方法參數中注入 注意這跟工廠差在 這是行為 工廠是new 不同 簡易工廠不一定有同樣方法 但抽象工廠才會有一樣方法 範例: 訂單希望提供數種物流選項以計算運費,但每家一種都要修改訂單類別的程式碼 ``` class Order { public function calculateFee($shipmentName) { switch ($shipmentName) { case 'Hsinchu': // 新竹貨運 $fee = .... ; break; case 'BlackCat': // 黑貓貨運 $fee = .... ; break; case 'PostOffice': // 郵局 $fee = .... ; break; default: break; } } } ``` https://github.com/3masterplus/book/blob/master/%E5%A4%A7%E8%AF%9D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F(%E5%B8%A6%E7%9B%AE%E5%BD%95%E5%AE%8C%E6%95%B4%E7%89%88).pdf ![](https://i.imgur.com/lBlVbo2.png) ![](https://i.imgur.com/YPNElcq.png) ![](https://i.imgur.com/Zz2XG8R.jpg) 一樣的抽成抽象父class 不同行為堆在同一個class就很容易用switch 但如果一個一個封裝就可以避免 策锐封裝變化 ## 抽象工廠(Abstract Factory) 工厂都可以生产 button 和 border,那么把创建这两个产品的方法抽象出来,这个抽象出来的方法叫做抽象工厂类,两个工厂都要实现这个抽象类 有共同的方法類型,但實作方式不同,得出來的結果也可能不同 e.g. 棒球打者每個人都需要打擊(共同的方法類型) 但每個人的打擊姿勢都不同(實作方式不同) 每個打擊者可能擅長打出滾地球或是高飛球(得出來的結果也可能不同) ## ENV 模式 laravel https://www.youtube.com/watch?v=wXwiztqJFdM&ab_channel=LaravelDaily https://juejin.cn/post/6997230227868876830 ## crobtab ![](https://i.imgur.com/QVUWAhb.png) 還沒試過 必須多安裝一個ext ## 生成器(Builder) 類似的產品,有類似的複雜的建造流程 用interface去規劃藍圖 ![](https://i.imgur.com/oGjUHrA.png) 像這個 我只要drive 沒就去做出來執行 有的話就去執行 執行啥呢 按照規規劃走 pizza要配料 要考過 要給人 就按照流程那樣 ## 工廠方法(Factory Method) 生產方式類似、少部分有些許的不同 以laravel來說每個view()這種就是 如果你建構她要很多預設 那他就很適合用工廠 不關心來源 只關係如何make 預設不只說是建構式喔 function也是 ![](https://i.imgur.com/WCRvLep.png) validate也是這樣 ![](https://i.imgur.com/h2blxZt.png) ## 原型(Prototype) 產生新物件成本過高時,直接複製先前生成的原型物件 ## 簡單工廠(Simple Factory) https://kejyuntw.gitbooks.io/design-pattern-for-php/content/common-design-pattern/creational/common-design-pattern-creational-simple-factory.html 簡易工廠不一定有同樣方法 工廠是建立 動作不要用工廠 要用測瑞 把不同點封裝起來 主要是把決定new哪個做成一個class可以看上面OOP截圖 封裝這個class讓他來決定 並保有擴充跟單一原則 但抽象工廠才會有一樣方法 我們如果想要煮東西給自己吃,我們需要自己「準備食材」、「烹煮食材」、「上菜」 ``` <?php /** * 抽象"烹煮"類別 */ abstract class Cook { // 準備食材 public abstract function prepareIngredient(); // 烹煮食材 public abstract function cooking(); // 上菜 public abstract function serve(); } ?> ``` 假如我們想要吃咖哩,我們會這樣做: ``` <?php /** * 咖哩 */ class Curry extends Cook { // 準備食材 public function prepareIngredient() { echo "準備「馬鈴薯」、「蘿蔔」、「洋蔥」、「肉」\n"; } // 烹煮食材 public function cooking() { echo "下鍋炒肉,等肉熟之後,將「馬鈴薯」、「蘿蔔」、「洋蔥」加到鍋裡並加滿水,等水滾加入咖哩塊悶熟即可\n"; } // 上菜 public function serve() { echo "盤子放上白飯,將咖哩淋在白飯周圍即可\n"; } } $curry = new Curry; $curry->prepareIngredient(); // 準備食材 $curry->cooking(); // 烹煮食材 $curry->serve(); // 上菜 ?> ``` 很好用 都用在同一個東西 不同type那種感覺 例如上次寫的 Excel 不同的excel 但都是excel 例如 計算機crud的也是 加減乘除都是計算 可以用工廠去做 因為這樣可擴充 之後可能還會增加 開根號之類的 ## 工廠策列結合 ![](https://i.imgur.com/dSo7P3k.png) 同一個父類別 然後type區分 這樣好處是 不同點封裝 一樣點一樣去跑 ## Command Pattern 命令模式 https://www.youtube.com/watch?v=GQzfF5EMD7o 像是加減乘除 你可以拆成四個 然後組合 重點可以 當前的 - add 的 或 當前的 +當前的 感覺就是單一封閉的進階 單一是可以擴充 這是單一可以反向 所以我覺得就是拆細閱好就對了拉 舉例 save exit save and exit 前面兩個命令 後面就能使用他們兩個 減少重複 感覺跟公司用的依樣 這樣會比較累 但之後會比較輕鬆 ## Facade Pattern https://www.youtube.com/watch?v=fHPa5xzbpaA fetch替換成axios 這模式是為了方便替換 有點OOP的五大原則中的替換原則 ## Singleton Pattern ![](https://i.imgur.com/BzxEfmH.png) 單例模式很適合在測試時候使用 單例模式基本上是一個美化的全局變量 ## Builder Pattern 很像公司那樣 定義好抽象 上層就只要選要得去建立 ## povider pattern 就像pizza連鎖店一樣 他負責pizza所有的東西 包含經理人員 如何去做之類的 基本上會像package一樣 負責所有的東西 ###### tags: `觀念重點區`