 [上篇:程式設計範式與程式語言](https://hackmd.io/xKXUypGtSWmwdeBeDcy3rg) # 抽象 * = 減法 + 除法 * 減法:去非本質及無關部分 * 除法:乘法逆運算(同類複製) = 同類合併 * 越抽象 = 設計面越重,反之實作面越重 * 分析:關注「是什麼(What)」X「怎麼樣(How)」 * 設計:模型建構 & 功能規範 * 做什麼 > 怎麼做 * OOA以物件為中心,而非程序 * OOD以介面為中心,而非實作 * OOP以資料為中心,而非演算法 ## 抽象資料 (資料封裝) * 廣義:打包 * 狹義:+存取控制(Access Control)。白箱變黑箱 * 資訊洩漏問題 * 隨時更換內部表示,不影響對外介面 ### 安全性 * 淺拷貝 Shallow Copy * 逐位元複製 Bitwise Copy * 效率 * 深拷貝 * 保護資料 * 遲拷貝 Lasy Copy * 起初淺拷貝,當有修改時轉為深拷貝 Copy-on-Write CoW #### 關心值 -> 拷貝 #### 關心同一性 -> 參照 ```csharp object a = 1 ; object b = 1 ; // 同一性 bool sameObj = (a == b ); // false // 值相等 bool sameValue = ( a.equals( b ) ); // true ``` ```csharp object a = "1" ; object b = "1" ; bool sameObj = (a == b ); // true bool sameValue = ( a.equals( b ) ); // true ``` * java string本身就是參照 ## 抽象介面 * 抽象:提煉物件最本質、最不易變化 * 封裝:隱藏非本質、易變化的部分 * C++透過介面繼承減少類別再編譯期的相依性,避免更動原本程式碼更動造成專案重建 * 開閉原則(OCP) > 能保證程式碼永遠不變的軟體有更好的歸宿: > 要嘛退化為硬體 > 要嘛進化為人體 * !!!過度追求重用 * 重用類別或方法發生改變->所有重用接受牽連 * C++ friend修飾子 * ~C# Partial 1. 自動Gen Code * 單向授權 * 不是打破封裝 因為在設計內對客戶端仍是封裝的 * 朋友函數 * C# obsolete -> 誰能保證設計的介面永遠不會過時 * 規範=語意上契約 * 編譯器管不著 * 靠單元測試 * 類別純粹和完備是相對概念 * 智慧型手機,早期認為不純粹,後期認為都是標準,普通手機反而不完備 * 大而全的整個類別在結構中處於頂端 * O 享受服務 * X 提供服務 * FUN:橫穿馬路 * 行人不該破壞道路的封裝性 * 交通設計不便=功能不完備->行人違規誘因 # 繼承 * is-a只是判斷的一種必要條件 * behaves-like-a描述可能更貼切 ## 語法 ### 1. 繼承實作+介面 ### 2. 繼承介面 ### 3. 繼承實作 * 通常同時繼承介面 * 僅繼承實作 C++ 使用private繼承 ```cpp= class Engine { public : void Start() {...} }; class Car : private Engine {}; ``` * 客戶取不到Engin的Start() * public繼承將具有父類別介面 = 實作介面 * 若想要使用Engine介面還可以 1. 手動增加介面 2. 將基礎方法對外開放 ```cpp class Car : private Engine { public : using Engine:Star ; } ``` ## 語意 ### 類別繼承 * 繼承實作+介面 * 重用舊程式碼 ### 介面繼承 * 介面重用 X程式碼重用 * 標記介面 * eq:Cloneable、Serializable... * 參與該類別所參與的任何運算 * 新程式碼能被重用 ## 子類別 != 子類型 ### 類別 * C++私有繼承 = 純子類別 != 子類型 * 偏重語法 * 偏重實作 ### 類型 * int = long 子類型;float = double 子類型。但都不是類別 * 可替代性 符合**里氏代換原則(Liskov Substitution Principle)** * 里氏代換原則 基於 類型 * 偏重語意 * 偏重介面 ### 里氏代換原則(Liskov Substitution Principle) * 規範抽象從單個類型擴充到類型模組 * 保證多型抽象 * <font color=red>長方形類別 vs 正方形類別;橢圓形類別 vs 圓形類別</font> * 子類型應保持或強化超類型的規範 * 不能強化超類型的先驗條件 * 不能弱化超類型的後驗條件 ## 小結 * 語法與語意盡可能契合,靠規範 -> 減少程式碼的邏輯隱患 * 分離類別介面與實作 1. 介面(API)/實作 * 語意:資料抽象 -> 介面(API) * 語法:封裝隱藏實作 2. 介面(Interface) * 語意:多型抽象 -> 介面 * 語法:繼承隱藏實作  ## 繼承規範 * 當心子類別呼叫父類別,父類別多型呼叫子類別 (公有方法與多型方法矛盾) * 職責上避免對外服務又對內服務 * 對外純公有 * 對內可多型 * 解方1:合成實作 * X:手工逐個開對應窗口 * O:修繕良機(重新規範) * 解方2:介面繼承 * 解方3:修飾詞限制類別級別(C#:sealed、Java:final) * 解方4:範本模式(Template pattern) * 埋hook ```Java protected void doFunction(){...} ``` * 強化版: ```Java protected void doFunction(){...} protected bool beforeFunction(){ return true; } protected void afterFunction(){} ``` * 對外公有方法 ```Java final public void function() { if(beforeFunction()) doFunction(); afterFunction(); } ``` * 推薦方法修飾子 | 修飾子\職責 | 公開介面 | 內部掛勾 | 內部介面 | 自用 | | - | - | - | - | - | | 存取控制 | public | protected | internal | private | | 多型 | 非多型 | 多型 | 非多型 | 非多型 | ### 案例 ```java= /* java.util.Stack的改進版(multiPush依賴push) */ public class Stack<E> { private Vector<E> data = new Vector<E>(); public void push(E item) { data.addElement(item); } public void multiPush(E... items) { for(E item : items) push(item); } public E pop() { E item = peek(); data.removeElementAt(size() - 1); return item ; } public E peek() { int size = data.size(); if(size ==0) throw new EmptyStackException() return data.elementAt(size - 1); } public int size() { return data.size(); } } ``` ```java= /* 計入推入總次數的堆疊(原始版) */ public class CountingStack<E> extends Stack<E> { private int pushCount; // 推入總次數 @Override public void push(E item) { ++pushCount; super.push(item); } @Override public void multiPush(E... items) { pushCount += items.length ; super.multiPush(items); } public int getPushCount() { return pushCount; } } ``` ```java= public class TestCountingStack // 測試程式碼 { public static void main(String[] args) { CountingStack<String> s = new CountingStack<String>(); s.push("first"); s.multiPush("second","third"); System.out.printIn(s.getPushCount()); } } ``` #### 輸出錯誤5 ```java= /* 計入推入總次數的堆疊(嘆號版) */ public class CountingStack<E> extends Stack<E> { private int pushCount; // 推入總次數 // 透過註解取消以下方程式,其他與原始版相同 /* @Override public void multiPush(E... items) { pushCount += items.length ; super.multiPush(items); } /* } ``` #### 輸出正確3 ```java= /* java.util.Stack的改進版2(multiPush與push互相獨立) */ public class Stack<E> { ... public void multiPush(E... items) { for(E item : items) data.addElement(item); } ... } ``` #### 輸出錯誤1 # 多型機制 ## 類型 ### 1. 通用多型 (Universal Polymorphism) * 克服靜態類型嚴格的語法限制 #### 1-1. 參數多型 (Parametric Polymorphism) * GP範式設計 * 靜態連結 * 演算法的普遍性 * 相同實作程式碼應用不同場合 #### 1-2. 包含多型 (Inculsion Polymorphism) * =子類型多型(Subtyping Polymorphism) * 動態連結 * 介面與實作分離 * 不同實作程式碼應用相同場合 ### 2. 特別多型 (Ad-hoc Polymorphism) #### 2-1. 強制多型 (Coercion Polymorphism) * 一種類型當作參數傳遞 * 隱式轉換類型 #### 2-2. 多載多型 (Overloading Polymorphism) ### 範本模式重在穩定節固的骨架 ### 策略模式重在靈活多變的手腕 ## 抽象類型 * 合成 vs 繼承 * X產生子類型 * X複寫基礎類別 * X存取protected ### mixin * ~抽象 B有基礎函式可用 * C# Extension Method * C++ ```cpp= /* 一個不可複製的類別 */ class NonCopyable { protected: // 非公有建構式防止創建物件 NonCopyable(){} // 非公有非虛擬解構式建議子類別非公有繼承 ~NonCopyable(){} private: // 私有複製建構式防止直接的顯式複製和透過參數傳遞的隱式複製 NonCopyable(const NonCopyable&); // 私有只遞運算子防止透過指定來複製 const NonCopyable& operator=(const NonCopyable&); }; /* NonCopyable的一個私有繼承類別 */ class SingleCopy : private NonCopyable(){}; /* 測試程式碼 */ int main() { SingleCopy singleCopy1; SingleCopy copy(singleCopy1); // 編譯器警告:企圖複製singleCopy1 SingleCopy singleCopy2; singleCopy2 = singleCopy1; //< 編譯器警告:企圖複製singleCopy1 return 0; } ``` ### Java/C#的抽象類別與介面語意上區別 ||介面|抽象類別| |-|-|-| |關係|can-do|is-a| |共性|相同功能|相同種類| |特徵|邊緣特徵(亦可核心特徵)|核心特徵| |聯系|橫向聯繫|縱向聯繫| |重用|規範重用|程式碼重用(亦可規範重用)| |實作|多種實作|多級實作| |重點|可置換性|可擴充性| |演變|新增類型|新增成員| * 介面=功能規範集合 * -able結尾命名 * 抽象類別=物件屬性抽象 * 名詞命名 ### 標記介面 1. 定義類型 2. 類型元資料載體 * 屬性導向程式設計(@OP) * Java annotation * C# attribute # 記憶體 1. 靜態分配(Static Allocation) * 編譯期處理 1. 全域變數 2. 靜態變數 3. 常數變數 2. 堆疊分配(Stack Allocation) * 執行期處理,但編譯期可以確定大小及生命週期 * 處理局部變數或自動變數 * 局部 - 強調有效範圍 * 自動 - 編譯器管理分配釋放 * O:效率高 * X:大小有限、提前確定、堆疊溢位 3. 堆積分配(Heap Allocation) * 執行期處理 * 處理new、malloc動態分佈 * X:記憶體碎片、元資料開銷、記憶體洩漏 * 元資料 matadata:存取目標資料的資料 ```csharp SomeType a ; // 將a分配在堆疊上 a = new SomeType(); // 在堆積上分配一個物件,將其參照指定給a ``` # 值 vs 參照 ||儲存內容|邏輯指代|價值屬性|物件複製|實現多型|空值| |-|-|-|-|-|-|-| |值|資料|直接|內在價值|指定|否|無| |參照|位置|間接|使用價值|克隆|能|有| ### Java * 無法自訂義值類型 * 所有物件都是參照類型 * 僅傳值=物件的參照值。X傳參照指的物件 * 複製=物件參照 * 複製值:重建物件、Clone、序列化 ```java class TestPassByRef { static void change(String str){ str = "new value"; } public static void main(String[] args) { String s = "old value" ; change(s); System.out.printIn(s); // old value } } ``` ### C++ * 無法自訂義參照類型 * 支援傳值&址 ```css // 分配在堆積上(c++) SomeType* a = new SomeType(); // 分配在堆疊上(c++) SomeType a = SomeType(); //< SomeType a ; // 傳遞參照 static void change(string &s){ s = "new value"; } ``` ### C# * 兼收並蓄 * class參照類型 -> 堆積 * struct值類型 -> 堆疊 * 支援傳值&址 ```csharp static void change(ref string s){ s = "new value"; } ``` # 語意類型 * 語法上的值類型在語意上可能是參照類型 * 語法上的參照類型在語意上可能是值類型 ex1 ```cpp // 值類型的ValueType具有參照語義(C++) ValueType v1 = someValue ; ValueType& v2 = v1; // 方法1:透過參照讓v2成為v1別名 ValueType* V3 = @v1; // 方法2:透過指標讓v3指向v1 ``` ex2 ```javascript // 參照類型的ReferenceType具有值語義 (假定它是Java的Clonable類別) ReferenceType r1 = someObject ; ReferenceType r2 = (ReferenceType)r1.clone(); //< 避免r1與r2共用同一物件 ``` ### 語意上如何判定類型 * 物件的複件能否替代原件 * Y:值語意 * N:參照語意 * 值透過具體資料描述抽象屬性 * 參照透過抽象方式指具體實體 * 物件若沒有識別碼,就會特徵模糊 * OOP物件三大特性:狀態(state)、行為(behavior)和識別碼(Identity) ex 介紹女友 ``` 對方的年齡、學歷、身討、體重 腦海立刻浮現出美女形象 (值語意的抽象) 迫不急待想見面 (參照語意的具象) ``` ### 補充 * Java和C#的String是參照類型,卻是值語意 * C#多載=運算子 * C++的string本就是值語意 * const 確保常數的正確性 ex java ```java String s1 = "ab"; String s2 = s1 ; // s2的內容是"ab" assert(s1 == s2); // s1與s2指向同一物件 s2 += "cd"; // s2的內容是"abcd" assert(s1 != s2); // s1與s2指向不同物件 ``` ## 物件 vs 語法物件 vs 語意物件 * 《大話重購》值物件VO(Value Object) = 值語意物件 * 大多時候值物件&參照物件意指語意上 * 資料傳輸物物件DTO(Data Transfer Object) = 多層架構溝通的值語意物件 * 傳輸物件 -> 有什麼 * 值物件 -> 是什麼 * 參照物件 -> 是哪個 * Java & C#的參照非原始地址 * 不透明指標 * 保證記憶體安全性 * UML顯示物件之間關係 * 值物件多用於屬性 * 參照物件多用於關聯 * 合成 = 值語意的包含 * 聚合 = 參照語意的包含 # 設計原則 ## 間接原則 > Any Problem in computer science can be slove with another layer of indirection > But that usually will create another problem > -- David Wheeler 任何電腦問題均可透過增加一個間接(Indirection)層來解決 任何社會問題均可透過減少一個間接層來解決 * 抽象設計->間接設計  ### 三層架構  * 層級別的架構設計 * Layer邏輯上 * Tier物理上 ### MVC架構  * 用於互動式系統的設計 ### 三層+MVC  ## 依賴原則 * 依賴反轉原則DIP Dependency Inversion Principle * 控制反轉原則IoC Inversion of Control * 依賴注入DI Dependency Injection * 依賴搜尋Dependency Lookup ### 依賴反轉 > 高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。 > 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。 > ->可維護性 * 強調依賴抽象 * 程式碼穩定可維護 * 依賴關係轉移 * 高層制定規範,低層遵守 * 合成->多型合成 * 具體類別不宜被繼承 ### 控制反轉 > 控制權從使用者的應用程式->底層元件控制 > 為高層提供充分的服務 > -> 可重用性 * 確保底層元件合適的時機有合適的依賴 * 高層應用不耦合元件 * ex:callback函式 * 控制權無法100%都落在一邊,視情況 #### 依賴注入 > 1. 元件的管理交給外部 > 2. 服務過程中反向呼叫應用層 * 強調依賴來源外部 * 改成DI * 類別圖:合成->聚合 * 合成:值語意。負責建構解構 * 聚合:參照語意。外部負責 #### 依賴搜尋 > 主動獲取依賴 ## 內聚原則  * 內容耦合 = 資料存取修改 * 公共耦合 = 全域變數共用 ### 局部化原則 > 程式碼的物理緊密度與邏輯緊密度保持一致 ```csharp // example 1 int i = 0; ++j; for(i = 0 ; i < 10 ; ++i) doSomething(); // example 2 ++j; int i = 0; for(i = 0 ; i < 10 ; ++i) doSomething(); // example 3 ++j; for(int i = 0 ; i < 10 ; ++i) doSomething(); ``` example 3 > 2 > 1 * 適用於模組單位、區塊單位、非語法單位(適度空行&註解) **DRY原則 Don't Repeat Yourself** **SRP原則 Single Responsibility Principle** ```csharp // 線上購物系統 public class Customer {/*略*/} public class Product {/*略*/} public class Order {/*略*/} // 購物車 public class ShoppingCart { public void AddItem(Product product , int quantity) {/*略*/} public void RemoveItem(Product product , int quantity) {/*略*/} public void Clear() {/*略*/} public Order Checkout(Customer customer) {/*略*/} public bool ValidatePayment(Customer customer) {/*略*/} public string ToXml(Order order) {/*略*/} } ```  * SRP不符合可以透過ISP重購 #### ISP 介面隔離原則 Interface Segregation Principle > 不應強迫客戶依賴那些它們不用的方法 > 多個專用的介面比單純一個總介面更好  * 減少介面變化的副作用 * **SRP保證類別高內聚** **ISP保證介面高內聚** ## 保變原則 PV(Protected Variations) > 找出預計的變化或不穩定點,分配其職責以便利用穩定的介面來包裝 * 封裝就是類別層級的PV * 多型就是介面層級的PV * 變中求穩 * 維護性 * 開放封閉原則穩中求變 * 可重用性 * 著重高維護性 * 可重用性 * 靈活性 * 擴充性 * 理解性 ### 迪米特法則 Lod(Law of Demeter) * = 最少知識原則 LKP(Least Knowledge Principle) * 不要跟陌生人交談 * !接龍式的程式碼通常違背迪米特法則 * 應利用中介層隔離 * 避免因跨層而維護度下降 * 信任朋友,但不信任朋友的朋友 ## 通用職責分配原則 GRADSP General Responsibility Assignment Software Patterns/Principle |名稱|問題|答案| |-|-|-| |資訊專家<br>infomation Expert|職責分配的基本原則是什麼?|把職責分配給資訊專家-擁有完成該職責所需資訊的類別| |創建者<br>Creator|誰負責創建物件?|把創建類別A的實體的職責分配給類別B<br>如果以下之一成立:<br>B包含A<br>B聚合A<br>B紀錄A<br>B密切使用A<br>B擁有A的初始化資料| |控制器<br>Controller|誰負責處理系統事件?|把處理系統式件的職責<br>分配給代表整體系統<br>或是件發生的用例場景類別| |低耦合<br>Low Coupling|如何支援低依賴、高重用?|分配職責以保持低偶合度| |高內聚<br>High Cohesion|如何駕馭複雜性?|分配職責以保持高內聚| |多型<br>Polymorphism|當行為因類型而變化時,誰負責?|當相關的備選方案或行為因類型而變化,利用多型機制<br>把職責分配給行為變化的類型| |純虛構<br>Pure Fabrication|當你不想違背低耦合高內聚原則<br>卻又找不到合適的負責者時,該怎麼辦?|將一組高內聚的職責集合<br>分配給一個虛構的「行為」類別(即不代表問題領域的概念),以支援低耦合高內聚| |間接<br>Indirection|如何分配職責以避免直接耦合?|把職責分配給一個仲介物件<br>以避免兩個元件或服務直接耦合| |保變<br>Protected Variations|如何分配職責給物件、子系統或系統,以使這些元素的變化<br>不會影響到其他元素?|找出預計的變化點或不穩定點<br>分配其職責以便用穩定的介面來包裝| ## 類別級設計原則 SOLID |名稱|內容| |-|-| |單一職責原則<br>SPR(Single Responsibility Principle)|一個類別應當只有一個變更的理由| |開閉原則<br>OCP(Open-Close Principle)|軟體實體應對擴充開放,對修改封閉| |里氏代換原則<br>LSP(Liskov Subsitution Principle)|子類型必須能替代超類型| |介面隔離原則<br>ISP(Interface Segregation Principle)|不應強迫客戶依賴那些他們不用的方法| |依賴反轉原則<br>DIP(Dependency Inversion Principle)|抽象不應依賴細節,細節應當依賴抽象| ## 套件級設計原則 <table> <tr> <th colspan = "3">名稱</th> <th>內容</th> </tr> <tr> <td rowspan="3">內<br>聚</td> <td>發佈重用等價原則</td> <td>REP<br>(Release-Reuse Equivalency Principle)</td> <td>重用的粒度即是發佈的粒度</td> </tr> <tr> <td>共用閉包原則</td> <td>CCP<br>(Common Closure Principle)</td> <td>同在一個套件中的類別應對同類變化封閉</td> </tr> <tr> <td>共用重用原則</td> <td>CRP<br>(Common Reuse Principle)</td> <td>同在一個套件中的類別一起被重用</td> </tr> <tr> <td rowspan="3">耦<br>合</td> <td>無循環依賴原則</td> <td>ADP<br>(Acyclic Dependencies Principle)</td> <td>在套件的依賴圖中不允許有循環存在</td> </tr> <tr> <td>穩定依賴原則</td> <td>SDP<br>(Stable Dependencies Principle)</td> <td>朝著穩定的方向依賴</td> </tr> <tr> <td>穩定抽象原則</td> <td>SAP<br>(Stable Abstractions Principle)</td> <td>一個套件的抽象度應與其穩定度相當</td> </tr> </table> # 設計模式 ## 創建模式 ### 建構子缺點 1. 名稱與類別同名,缺乏表現力 2. 無法簽署相同 * eq:笛卡爾座標 vs 極座標。都是兩個double 3. 創建新物件 * new() 違反DIP的味道 5. 無法繼承、多型 6. 未必完全初始化 ### 工廠模式 1. 靜態工廠 (簡單工廠) * 靜態方法 (X:無法繼承) * switch case 2. 工廠模式 * 繼承工廠介面 * 虛擬建構式模式(Virtual Constructor Pattern) 3. 抽象工廠模式 * 一系列成套的產品,確保一致性 * 工具箱模式(Kit Pattern) ### 創建者模式 * 使用者組出最後產品 * VS 工廠 * 保護產品的製作來源 * 所得及最終產品 ### 物件池模式 ### 單例模式 ### 原型模式 ### 小結 * 避免使用者透過建構式操作底層 * 對創建邏輯進行封裝及抽象 ## 結構模式 * 層(Layer) = 抽象層次,不同層次系統單向依賴 * 區(Partition) = 同一抽象層次按功能分類,同層系統雙向合作關係 * 某層某區 * ~ C++ & C# namespace (邏輯層級) * assembly 物理層級 * ~ Java package ### 橋梁模式 * 結合緊密的介面與實作分離 (m*n -> m+n) ### 配接器模式 * 無關類別能合作 * = 包裝模式(Wrapper Pattern) ### 裝飾者模式 * 動態加減功能 ### 代理模式 ### 外觀模式 ### 複合模式 * 管理層級 ### 享元模式 * 抽象物件內在與外在 * 內在共用 ## 行為模式 ### 職責鏈模式 ### 命令模式 ### 觀察者模式 ### 仲介者模式 ### 狀態模式 ### 備忘錄模式 ### 訪問者模式 * 分派=呼叫點(Call Site)相應的呼叫方法 * 靜態分派:多載多型、參數多型 * 動態分派:子類型多型 * 單分派:僅取決於一個類型的分派 ```csharp= abstract class File { File parent ; public string Name { get ; set ;} public File Parent { get => parent ; set{ if( parent != null ) parent.Remove( this ); parent = value ; } } virtual public File[] List(){ return null ; } virtual public void Add( File file ){} virtual public void Remove( File file ){} } class PlainFIle : File { public long Size { get ; protected set ;} public PlainFIle( string name , long size ) { Name = name ; Size = size ; } } class Directory : File { List< File > children = new List< File >(); public Directory( string name ) { Name = name ; } override public File[] List() { return children.ToArray(); } override public void Add( File file ) { children.Add( file ); } override public void Remove( File file ) { children.Remove( file ); } } ``` ```csharp= interface FileVistor { void Visit( File file ); //< C#僅能單分派 void Visit( PlainFile file ); void Visit( Directory file ); } class FilePrinter : FileVisitor { public void Visit( File file ); { Console.Write( "abstract file: " + file.Name ); } public void Visit( PlainFile file ); { Console.Write( "plain file: " + file.Name ); } public void Visit( Directory file ); { Console.Write( "directory: " + file.Name ); } } ``` ```csharp= File dir = new Directory( "directory_name" ); File file = new PlainFile( "file_name" , 100 ); FileVisitor printer = new FilePrinter(); printer.Visit( dir ); printer.Visit( ( Directory )dir ); printer.Visit( file ); printer.Visit( ( PlainFile )file ); /* 輸出 * abstract file: directory_name * directory: directory_name * abstract file: file_name * plain file: file_name * / ``` * 純程序式可以動態多分派,OOP僅能靜態單分派 * 物件如何動態多分派 (不用耦合物件具體類型) * 訪問者模式->File貼加Accept(),引數為訪問者類型。 ```csharp= abstract class File { // 之前相同 abstract public void Accept( FileVistor vistor ); } class PlainFile { // 之前相同 override public void Accept( FileVistor vistor ) { visitor.visit( this ); } } class Directory { // 之前相同 override public void Accept( FileVistor vistor ) { visitor.visit( this ); foreach( var child in children ) child.Accept( vistor ); } } ``` ```csharp= File dir = new Directory( "directory_name" ); File file = new PlainFile( "file_name" , 100 ); FileVisitor printer = new FilePrinter(); dir.accept( printer ); // = printer.visit( ( Directory )dir ); file.accept( printer ); // = printer.visit( ( PlainFile )file ); ``` * File.Accept()單分派一次,FileVistor.Visit()再單分派一次 * 未來擴充性放在FileVistor繼承者上不用動到File子類別 ### 迭代器模式 ### 直譯器模式 ### 狀態模式 ### 範本模式 ## 模式比較 * 命令式OOP弱點:物件必須獲得另一個物件識別碼,才能發送訊息->類型、個體、時間耦合 * 管線模式可以克服 * 職責鏈模式∈管線模式 * 例外處理∈職責鏈模式 * 職責鏈->職責分解,重行為;裝飾者->職責結合,重結構 * 命令模式:呼叫OO化,中間有個管理者,請求與接收在空間及時間解耦 * 仲介者->資訊管理集中;觀察者->資訊分散處理 * 外觀模式->請求發送語接收是單向主客關係;外觀=索取子系統的門戶 * 仲介者=雙向的同事關係;仲介者=交流平台 * 狀態模式->物件狀態的演進;備忘錄模式->物件狀態的回歸 * 訪問者->走訪方式有限,通常不允許增減替換,處理元素能獲得具體類型;迭代器->多種走訪方式,得到元素類型比較抽象 * 直譯器模式∈複合模式。直譯器->語言表示和動態翻譯;複合模式->結構表示和靜態組成 ## 終結 * 書中與常見的程式設計比較 * 程式設計 -> OOP -> 可重用性 -> 設計模式 * 程式設計 -> 程式設計範式 -> 可維護性 -> 設計原則 * OOP僅是主流設計範式,並非唯一也並非所有場合都合適 * 建議**可維護性**優先可重用性 * 穩定的介面與規範才是程式碼可維護性的核心武器 * 所有設計原則中,最核心的原則是抽象 * 降低模組間耦合,強化模組本身內聚 * 簡化複雜性 * 演算法是程式的靈魂,軟體的核心競爭力
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up