---
# System prepended metadata

title: 程式設計範式與OOP的思考術：冒號老師的十三堂課 - 下篇：抽象機制與物件範式
tags: [薛威明, 讀書會]

---

![image](https://hackmd.io/_uploads/HklIGL13C.jpg)

[上篇：程式設計範式與程式語言](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)
        * 語意：多型抽象 -> 介面
        * 語法：繼承隱藏實作
    ![螢幕擷取畫面 2024-10-21 093149](https://hackmd.io/_uploads/rJb7yNml1x.png)

## 繼承規範
* 當心子類別呼叫父類別，父類別多型呼叫子類別 (公有方法與多型方法矛盾)
    * 職責上避免對外服務又對內服務
        * 對外純公有
        * 對內可多型
    * 解方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)層來解決
任何社會問題均可透過減少一個間接層來解決

* 抽象設計->間接設計
![inderection](https://hackmd.io/_uploads/SJLYSjq9Jx.png)

### 三層架構
![Three Layer](https://hackmd.io/_uploads/Hkw5ri95Jx.png)
* 層級別的架構設計
    * Layer邏輯上
    * Tier物理上

### MVC架構
![MVC](https://hackmd.io/_uploads/H1QsSocqkx.png)
* 用於互動式系統的設計

### 三層+MVC
![ThreeLay_MVC](https://hackmd.io/_uploads/rJy3ro5cyl.png)

## 依賴原則

* 依賴反轉原則DIP Dependency Inversion Principle
* 控制反轉原則IoC Inversion of Control
    * 依賴注入DI Dependency Injection
    * 依賴搜尋Dependency Lookup

### 依賴反轉

> 高層次的模組不應該依賴於低層次的模組，兩者都應該依賴於抽象介面。 
> 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。
> ->可維護性
* 強調依賴抽象
    * 程式碼穩定可維護
    * 依賴關係轉移
        * 高層制定規範，低層遵守
* 合成->多型合成
    * 具體類別不宜被繼承

### 控制反轉
> 控制權從使用者的應用程式->底層元件控制
> 為高層提供充分的服務 
> -> 可重用性
* 確保底層元件合適的時機有合適的依賴
    * 高層應用不耦合元件
    * ex：callback函式
* 控制權無法100%都落在一邊，視情況
    

#### 依賴注入
> 1. 元件的管理交給外部
> 2. 服務過程中反向呼叫應用層
* 強調依賴來源外部
* 改成DI
    * 類別圖：合成->聚合
        * 合成：值語意。負責建構解構
        * 聚合：參照語意。外部負責

#### 依賴搜尋
> 主動獲取依賴

## 內聚原則
![螢幕擷取畫面 2025-03-13 114309](https://hackmd.io/_uploads/HyUsSRy3yg.jpg)

* 內容耦合 = 資料存取修改
* 公共耦合 = 全域變數共用

### 局部化原則
> 程式碼的物理緊密度與邏輯緊密度保持一致
```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) {/*略*/}
}
```
![螢幕擷取畫面 2025-03-13 114341](https://hackmd.io/_uploads/HkttSRkh1e.png)

* SRP不符合可以透過ISP重購
#### ISP 介面隔離原則 Interface Segregation Principle
> 不應強迫客戶依賴那些它們不用的方法
> 多個專用的介面比單純一個總介面更好

![螢幕擷取畫面 2025-03-13 114433](https://hackmd.io/_uploads/rJUdH01nye.jpg)
* 減少介面變化的副作用

* **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僅是主流設計範式，並非唯一也並非所有場合都合適
        * 建議**可維護性**優先可重用性
* 穩定的介面與規範才是程式碼可維護性的核心武器
* 所有設計原則中，最核心的原則是抽象
    * 降低模組間耦合，強化模組本身內聚
    * 簡化複雜性
* 演算法是程式的靈魂，軟體的核心競爭力