---
# System prepended metadata

title: 大話重構講義
tags: [薛威明, 讀書會]

---

###### tags: `讀書會` `薛威明`

# 大話重構講義

筆記：薛威明

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

[影片](https://www.youtube.com/playlist?list=PLDE-E73wU5urgMZvpCEUhbvYvq1-n1Z0Z)
[投影片-Part1 基礎篇](https://hackmd.io/@voxar/BJk_3Az12)
[投影片-Part2 實踐篇](https://hackmd.io/@voxar/S1efbNudDh)
[投影片-Part3 進階篇](https://hackmd.io/@voxar/S1A86bZx6)
[博客來連結](https://www.books.com.tw/products/0010687580)

# CH1 重構：改變既有程式碼的一劑良藥

## 什麼是重構

嚴謹而安全的過程
保證軟體改善同時，不會引入新的Bug

1. 不改變外部行為
    * 輸入輸出前後是一致的
    * 貫穿重構過程的是測試
    * 從手工漸漸轉為自動化
    * 一開始就自動化測試是不切實際
    * 測試就是系統重構的保險鎖

3. 改變內部結構，使其易於閱讀、維護和變更

### 兩頂帽子

1. 先只重構，不新增功能，有能力適應新需求
2. 再增加新功能，滿足新需求

### 小步快跑 vs 大格局

**每次重構週期：10min ~ 1hr**
* 週期越長 = 問題複雜 = 出錯機率高

# Ch2 重構方法工具箱

重構 = 一系列的等量變換

* 就像數學算式，每行算式都是等式，可以交換
* 過程雖然改變，但等號頭尾是不變的

### 第一次重構 - 註解、重新命名、調整次數、分行
解決：
* 沒有註解
* 命名不易懂
* 沒分行，可讀性低

#### 專注業務邏輯清晰

O：方法提煉 -> 類別、介面獨立
X：簡單千行程式碼或是注釋


### 第二次重構 - 抽取方法
解決：
* 可讀性低
透過函式說明
* 內聚性不佳
一個函式處理一件事

### 第三次重構 - 抽取類別
解決：
* 內聚性不佳
類別單一原則

### 第四次重構 - 抽取介面
解決：
* 擴展性不好
適應新需求

### 領域驅動設計 來設計/重構 

**領域驅動設計**( DDD - Domain-Driven Design )
物件導向設計的類別、方法、關聯，應當與現實世界中的事物、行為及相互關係對應起來
幫助合作夥伴好理解程式碼

## 小結

* 程式碼還是那些程式碼
* 執行結果還是那些結果
* 但結構發生變化
    * 可讀性 (函式、變數命名並加上註解)
    * 可維護 (重新組織函數到各自函式或物件中)
    * 易變更
* 實務上，簡單的需求不要「過度設計」
* 書籍附錄列出重構方法
《Refactoring: Improving the Design of Existing Code》

# Ch3 小步快跑的開發模式

**錯誤發現得越早，損失越小
大格局你傷不起**

* 需重構文件 - 設計錯了誰負責
* 過去維護解決的問題都在細節設計 - 易忽略
* 結果新系統=舊系統翻版 - 已修復問題再現
* 數月的光陰，成功機率50%

**專案管理就是要讓風險變小**

* 驗證範圍太大 -> 驗收時間越長 -> 風險越大

不同的人重構方式不盡相同

# Ch4 保險索下的系統重構

測試是保險索，確保每次重構是正確的
如果無法保證每一步都是正確的，小步快跑也不能解決風險

## 測試種類

1. 系統測試：透過文件，設計測試案例，往往是手工
2. 單元測試：自動化測試程式

#### 重構路上不要太早自動化測試，先手動再視狀況自動化
* 前人的程式已經難以理解、複雜、相依性過高
* 隨者重構深入，原本針對函式的測式將失效而遺棄
* 設計沒有分層，面對涉及資料庫、網路溝通，難以馬上自動化測試

## 新需求發生的時候，測試案例就需要更新

根據兩頂帽子原則
* 執行第一頂帽子時，每次重構後，測試案例都會直接通過
	* 程式碼整理
	* 抽取方法
	* 抽取類別
	* 抽取介面
* 執行第二頂帽子時
	* 實作了新需求，原本的測試案例就需要更新
	* 新的需求也需要新的測試案例

# Ch5 第一步：從分解大函數開始

抽取方法

### 超級大函數的產生

1. 起初不複雜、邏輯清晰、易讀、易懂
2. 業務邏輯一次次變更，不停添加
3. 開始有不合理的設計

## 1-a 分段重組+註解

* 關聯程式碼放在一起
* 相較獨立的程式編寫註解
* 變數宣告與真正使用的程式碼放在一起

## 1-b. 抽取程式碼

* 取出的函式命名
	* 重新命名多次 
		* getBlsj(int iCzyf) 開始理解不深
		* transformDate() 最後理解是時間轉換
	* O：使用者角度命名 X：開發者
	* X過於專業
* 抽出的程式碼是功能內聚的
	* 功能是說得清、道的明
* 只執行一個清晰的功能

### 可抽取的地方

1. 重複程式碼
2. 塊操作
	* 條件敘述
	* 迴圈語句
	* try語句

### 值物件

處理抽取函數與原函數的資料交換
* X長長的參數清單
* X：業務 O：純資料
* 初期雜亂無章，重構中逐漸改善
* 不建議返回值=值物件 -> 怕氾濫
	* 針對傳遞值物件修改

# Ch6 第二步：分拆大物件

抽取類別

## 超級大物件的產生

1. 如同超級大函式
2. 抽取方法後

## 如何拆解超級大物件

1. 小步快跑，先用方法抽象類別，不要想第一次就完美重構
2. 單一職責原則：一個職責就是軟體變化的一個原因。
	不同職責不同類別
	繼承拆分：相同業務抽象父類別，不同業務子類別實作

## 如何避免超級大物件 / 大函式

* 設計之初使用
	* 職責驅動設計
		類別和介面絕不做跟自己職責無關的事 (高內聚)
	* 分析領域模型
		模擬真實物件的屬性、行為與物件之間關係 (易理解->可讀性高)
* 新需求發生時，利用單一職責檢查業務變更原因是否增加，與原本業務是否無關，適時拆分類別
	A的變更不該影響B，兩者放置不同類別
* 尋找資訊專家
	擁有執行該方法所需資料的物件
	
### 超級大物件從 全能者 -> 管理者 / 協調者

# Ch7 第三步：提高程式碼複用率

## 降低重複程式碼
風險 > 超級大物件

無精心設計就直接照需求循序開發
O：首次開發時間大大降低
X：維護成本大大提升

### DRY原則
= 不要重複自己 = Don't repeat yourself
= **OAOO原則** = 一次且僅一次 = Once and only once
ctrl c + ctrl v 第一次還能容忍，拷貝第二次就該思考

### 如何識別相似相近的功能
1.同一流程某個環節採用不同方法 (eq. 多元付款的選擇)
2.不同業務某個功能相似相近環節 (eq. 各類操作都會事前檢查)
3.本身就相似相近的功能 (eq. 收款單&付款單僅是甲乙方互換而已)

### 提高程式複用的方法
1. 重複程式碼存在同一物件中 -- **抽取方法**
2. 重複程式碼存在不同物件中 -- **抽取類別**
	* 工具類別
	* 實體類別(業務邏輯，需要實體)
	* 降低散彈槍修改(Shotgun Surgery)
3. 各類別具有某種並列關係 -- **抽取父類別**
	1. 整理(抽取方法) 
	2. 比較(相似程式碼) 
	3. 抽取共用(子類實作)
4. 繼承氾濫 -- **轉換組合**
 排列組合關係 (M x N) -> (M + N)
5. 重複程式法被割裂成碎片 -- **樣板模式**
 SOP程式碼 
	* 父類別抽取不變步驟 
	* 子類別實作變異步驟
	
# Ch8 第四步：發現程式可擴展點

## 可擴展點

* 滿足開放—封閉原則的系統
> 	**開放—封閉原則 (OCP原則 - Open-Close Principle)**
	1. 功能擴展是開放的
	　　　新增物件、類別達到功能擴充
	2. 程式碼修改是封閉的
	　　　上線的程式碼是不會更動的，除非有bug
* 兩項帽子重構的第一步完成

### 原有程式碼與新程式碼有效隔離

* 面對修改，盡可能遵守OCP
* 面對重構，盡可能兩項帽子
* 設計之初不確定性多，暫時先不去處理，但是設計擴展點出來
	* **鉤子 (hook)** 空函式放入抽象類別中
		* 原有類別直接呼叫空函示，不影響既有功能
		* 新類別實作
* 面對擴展點
	1. 萬惡魔王if-else或是switch語句
		是否造成類別過多職責。內聚下降、耦合上升
		* 拉出類別 + 設定檔
	2. 面對相似操作步驟
		* 面板模式
			* 相同程式碼拉成抽象類別方法
			* 不同程式碼抽象類別統一名稱，繼承類別各自實作
	3. 操作前後有不確定設置檢查
		* 頗面導向設計 = Aspect Oriented Programming = AOP
		* interceptor攔截器 = 擴展程式
		利用參數檔加入複數功能
	4. 其他
		* 利用繼承滿足不同架構

# Ch9 第五步：降低程式依賴程度

* 目標：各功能像插座插頭一般關係，隨意插拔
* 實作：細微面靠設計模式(延伸閱讀)

## 依賴反轉原則(DIP：Dependency Inversion Principle)

1. 高層次模組不應該依賴低層次模組，兩者都依賴抽象介面
2. 抽象介面不應該依賴具體實作，具體實作應該依賴抽象介面 

**定義介面 = 契約** 解偶相互依賴關係
降低與實作類別耦合，讓類別成員好替換

### 工廠模式產生實作介面 解耦 實作類別

透過ID請工廠尋找特定類別並實作，使用者不用直接耦合實體
降低選擇實作類別的耦合程度，減少參與物件生成過程

### 適配器模式 解偶 外部介面

* Adapter：不相容的界面接在一起
* 應付plugin升級/替換

### 橋接模式 解決 繼承氾濫

* Bridge：多種變化形成繼承，改寫為組合 (M x N -> M + N)
* 拆出變因 -> 多元組合

### 策略模式 解耦 方法
### 命令模式 解耦 程序
### 透明功能擴展
* 組合模式
	* Unity GameObject上的Compoment
	* 水平擴展
	* 通常一對多
* 裝飾者
	* 包裝者
	* 鉛直擴展
	* 通常一對一

# Ch10 第六步：我們開始分層了

## 大型Web系統分層結構
![](https://hackmd.io/_uploads/ryvCNlx1p.png)

**前端介面**
* 網頁
* 對象：使用者
* 展示華麗介面
* 便捷操作

**MVC層** (Web層 / 展示層)
* 後台對前端資料的處理

**BUS層** (Business Logic Layer / 業務邏輯層 / 領域層)
* 真正編寫程式的地方
* 領域模型的實現

**DAO層** (Data Access Layer / 資料存取層 / 持久層)
* 資料庫新增、刪除、修改、查詢

**值物件**
* 所有層次統一資料傳遞

## 領域驅動設計

* 貧血模型 (Anemic Domain Model)
	* 領域物件=值物件不同分層傳遞
	* 大量薄型service類別

* 充血模型 (Rich Domain Model)
	* 領域物件擁有所有屬性+業務操作
	* 龐大臃腫

## 面對技術的變革
1. 決定引入的新技術調研
2. 遺留系統分析
	* 主要/次要
	* 容易/困難
3. 去除框架成本 
4. 小步快跑

# CH11 一次完整的重構過程
### 重構 ≠ 重新開發

### 1.分解大函數 
#### 抽取方法
* 註解
* 變數重新命名
* 段落整理
### 2.分拆大物件
#### 抽取類別
* SPR原則
* 注意if-else壞味道
### 3.提高複用率
* DRY原則
* 封裝工具類別
* 組合模式
### 4.發現擴展點
* OCP原則
* 工廠模式
* 樣板模式
* 鉤子
### 5.降低依賴度
* 設計模式
* 分散在前面步驟
### 6.分層
* 要保留分層重構時間
∵初期需求≠最終需求。
！自以為->未來風險
### 7.領域驅動設計

# CH12 什麼時候重構
## 重構是一種習慣
* 程式可讀性增高
* 程式複用性提高
* 與敏捷開發相輔相成

### 兩項帽子 先重構在擴展
* ∵新功能加入 -> 系統品質下降 -> 重構 / 重頭做
* 務必先重構讓系統維持品質，遵循OCP原則

### 緊急任務時 又如何重構
時間不夠、範圍又太深太廣
**做完整的重構設計**
**只完成最緊急的部分**
* 紀錄未完成開發部分
* 紀錄其他沒時間處理的重複部分

**遏制糟糕設計蔓延**

# CH13 測試驅動開發
* TDD = Test-Driven Development
* 極限程式設計的重要部分
	* XP = eXtreme Programming

* 同一次開發，不必刻意遵循OCP原則
* 針對遺留系統重點測試即可(範圍限縮)
	* 經常維護
	* 複雜度高
	* 易出錯
	* 建立靜態類別圖分析關係

## 個人淺見

* 測試是重構的保險
* 先寫測試後寫測試都比沒有好
* 易出錯、複雜的地方竟然佈滿測試

# CH14 全面的升級任務

## 恰如其分規劃的演進式開發
### 計畫式設計 = 瀑布式開發。
* 更動規則須寫在合約
* 閉門造車，風險最後顯現
* 計畫趕不上變化
### 演進式設計 = 反覆式開發。
* 搭配自動化測試、系統重構
* 走一步算一步，缺乏長遠的規劃

#### 個人見解
	大架構粗略的計畫式設計
	頻繁的演進式設計，每次洽又是小架構的稍慎密計畫式設計
	為敏捷訂好時程目標，每次敏捷就是小瀑布開發

## 風險驅動設計 -> 全面升級

1. 採集和識別風險
2. 每個風險制定解決方案
3. 評估優先順序
4. 有計畫的一步一步改進

# CH15 我們怎麼擁抱變化

## 領域模型分析方法

* 領域模型
	* 學習、掌握業務領域的規則
	* 圖形化抽象模型 
		* 一系列類別圖
		* 誰來繪製？整個團隊討論
* 分類
		1. 原文分析法
		2. 領域驅動設計

## 1. 原文分析法
Textual Analysis

#### 1.1. 產生案例
* 觸發事件
* 前置條件
* 事件流
	* 基流
	* 分支流
	* 替代流
* 後置條件

![未命名 -2.jpg](https://hackmd.io/_uploads/SyJtNpKXp.jpg)

#### 1.2. 分析事件流文字描述
* 提取名詞 
* 刪除名詞 (不符合核心領域)

#### 1.3. 轉換名詞
* 類別
* 類別的屬性

#### 1.4. 原文動詞分析
* 類別的方法
* 類別的關聯

#### 個人見解
* ~UML開發設計 
* User Case -> Class Diagram
* Other UML Diagram

## 2.  領域驅動設計
Domain-Driven Design (DDD)

### v.s. 原文分析
* 可以客戶參與建模，一起形成統一語言
* 混和語言 = 軟體技術元素 + 業務領域術語
* 圖形化模型
	* 類別圖方式描述問題
	* 業務視角繪製

##### 書中範例截圖15.7、15.8 (書中關係圖有誤，此為修正後)
![大話重構1](https://hackmd.io/_uploads/rkfJEsZE6.png)
![大話重構12](https://hackmd.io/_uploads/HyPJ4s-E6.png)

### 持續學習的開發旅途
* 研發人員不必是專家，也不用一夜成專家
* 透過專家，需要時間學習領域知識
* 軟體隨時間進化升級

### 領域模型 vs 領域層
* 領域模型
	* 只考慮需求
	* 不考慮技術實現
* 領域層
	* 屬於軟體系統的分層
	* 盡可能與領域模型相似
	* 考慮到技術實現的可行性

## 遺留系統中如何應用
1. 從遺留使用者手冊開始，操作系統，認識系統
2. 記錄過程，寫案例說明
	* 逐漸改善的過程
	* 專注在閱讀函式，而非程式碼
	* 做**閱讀用**的重構
		* 很快會被遺棄，when真重構時
3. 小步快跑，小範圍實施領域模型
4. 系統運行的好好的，就不用修改

# CH16 測試的困境

## 安全重構

* 不會帶來新Bug的重構
* 不能安全重構，永遠只能看熱鬧，無法參與其中(尤其遺留系統)
* +自動化測試

----

### 資料庫狀態變化
1. 資料庫與被測程式解偶 
	* 遺留系統難馬上開始
3. 保持每次測試資料庫狀態一致 


### WEB、硬體
1. 合理剔除WEB層，再BUS層測試 
	* 遺留系統難馬上開始
3. 系統測試
	* 模擬使用者操作
	* QTE (from HP)

## 自動化測試 無法完全取代 手工測試
* 大量的Bug依然手工測試發現
* 自動化目的
	* 即使內部發生變化
	* 外部環境依然一致
	* 結果還是正確
* 當條件被打破
	1. 手工測試
	2. 調整測試程式碼
	3. 通過測試

## 寫測試。開發人員? 還是測試人員?
### 開發人員寫
* 單元測試級別上與程式設計實作息息相關
* 可能會忙死 (**測試開發工程師** 新職位的誕生)
* 天生樂天，很少思考什麼情況不OK，不全面

### 測試人員寫
* 天生悲觀。測試全面，挑Bug找毛病
	* 懸崖測試
	* 覆蓋性測試
	* 隨機測試
	* 探索性測試
* 可能不善寫程式

### 敏捷開發團隊

* 打破了開發與測試之間的牆，一起工作
* 職責不斷交替
* 但大多公司，開發就是開發，測試就是測試

### 截長補短

* 初期由開發人員，甚至測試驅動開發
* 中期交測試人員
	* 先手工測試
	* 有價值的測試轉開發人員
	* 自動化測試

# CH17 系統重構的評價

## 1.超級大函數數量
* 網路參考值：10、20、50行(不算括號、註解)
* JAVA工具：checkstyle、PMD、FindBugs

## 2.大物件數據
* NPM：Number of Public Methods for a class
	* 一個類別有多少個公共方法
* JAVA工具：checkstyle、PMD、FindBugs

## 3.相同程式碼再多處複製
* JAVA工具：checkstyle、PMD、FindBugs

## 4.低耦合、高內聚
* 内聚難測量
* 耦合可測量

### a.圈複雜度 (cyclomatic complexity) 
* ~至少需要多少測試案例，才能跑遍某函式所有程式分支
* if複雜度高->該方法較高耦合與較低內聚
* 流程圖，計算公式V(G) = e - n + 2
	* e：邊、n：點
	* 網路建議不要超過10
* 複雜度反映判定節點上，即流程圖的區域數
	* V(G) = 區域數 = 判定節點數 + 1

#### 範例
![螢幕擷取畫面 2023-12-07 095816](https://hackmd.io/_uploads/H1r1DiRSa.png)
---

* 分析上常常是類別級別
	* 總圈複雜度
	* 平均圈複雜度
* JAVA工具：JavaNCSS、Cobertura、ckjm

### b.傳入/傳出耦合
* 傳入耦合 afferent coupling
	* 被import
	* 較有價值
	* 高表示設計合理
* 傳出耦合 efferent coupling
	* import別人
	* 評價簡單
	* 高表示分拆與解偶
* 不穩定性 = 傳出耦合度 / 總耦合度
	* 依賴其他類別程度

### c.繼承
* DIT：Depth of Inheritance Tree
	* 繼承樹深度
* NOC：Number of Children
	* 繼承樹子節點的數量
* 兩指標高表示**繼承氾濫**

# 附錄 重構方法
## 1 重新組織函數

1. 抽取方法
2. 行內方法 
	* 抽取方法反程序
4. 行內臨時變數 
	* 不透過中間變數直接使用方法
5. 以查詢替換臨時變數
	* 將運算邏輯抽成方法直接使用
7. 引入解釋性變數
	* 複雜運算式拆斷並命名變數
6. 分解臨時變數
	* 同一變數多次使用，改為創造許多個解釋性變數使用
7. 移除對參數的設定
	* 方法傳入的參數不修改，而是回傳一份新的
8. 以方法物件替代方法
	* 將方法拉出成為方法類別使用 
9. 替換演算法

## 2 在物件間遷移
1. 遷移方法
	* 依職責把方法搬到對應的類別中
2. 遷移欄位
	* 依職責把欄位搬到對應的類別中
3. 抽取類別
4. 行內類別
	* 抽取類別反向操作
5. 隱藏的委託關係
	* 同時使用有潛藏關係的兩類別
	* 僅使用其中一個，透過其控制另一個
6. 移除中間人
	* 隱藏的委託關係的反向操作
7. 引入外加的方法
	* 方法多載等，使操作方法的程序簡化
8. 引入本地擴展
	* 繼承類別達到某些方法類別的存取
	* OCP

## 3 重新組織資料
1. 自封裝欄位
	* 使用getter & setter
2. 以物件取代數值 
	* eq 地址 address、city、street
3. 將值物件改為物件的參考
	* 提供Repository，避免大量創造物件
4. 將物件的參考改為值物件
	* 避免操作上干擾，降低複雜度
5. 以物件取代陣列
	* object[] -> class
6. 複製被觀察資料
7. 單向關聯變為雙向
	* 員工有部門欄位
	* 讓部門也有員工欄位
	* 易搜尋
8. 雙向關聯變為單向
	* 避免閉環
9. 特殊數字改為特定符號常數
10. 封裝欄位
11. 封裝集合物件
	* add & remove
12. 將記錄替換成值物件
	* xml、json轉換
14. 由類別替代類型代碼
	* 使用類別或列舉取代類型代碼(string)   
15. 由子類別替代類型代碼 
	* eq 員工類型：程式設計師、銷售員
16. 由狀態/策略模式替代類型代碼
	* eq 員工類型算薪水，運用不同策略發薪
17. 替換子類別為欄位
	* 繼承氾濫
	* eq 男&女類別 -> 人類(性別)

## 4 簡化條件運算式
1. 分解條件運算式
	* 條件式封裝方法或類別，用解釋語句命名 
2. 合併條件運算式
	* 返回結果相同的分支合併
3. 合併條件中重覆的部分
	* 分支內重覆部分拉到條件式外
4. 移除控制標記
	* 迴圈中常透過變數當Flag，將其改回判斷式
```csharp=
bool flag = true ;　　　　　　　　　　　　　　while( ... ) {
while( flag ){　　　　　　　　　　　　　　　　　　...
	...　　　　　　　　　　　　　　　　　　　　if( ... ) break ;
	if( ... ) flag = false ;　　　　　　 }
}
```
5. 將巢狀條件替換為多條檢查並返回之語句
	* retutn寫在各分支，而非函式尾端
6. 用多型替代條件
	* 條件式程式封裝子類繼承共同父類或介面  
7. 引入null物件
	* 執行結果可能為null，但後續不但被操作
	* 會需要一值被檢查null，不如製做null物件
8. 引入斷言
	* 將複雜判斷式封裝成函式   

## 5. 簡化呼叫方法

1. 重新命名方法名稱
2. 增加參數
3. 縮減參數
4. 分離查詢與更新
	* 將函式分拆成查詢函式及更新函式
5. 參數化方法
	* 類別中多個方法相似，將其合併
	* 建立一個參數類別囊括所有變數
	* 透過參數來表達不同的地方
6. 將參數替換成明確的方法
	* 利用解析參數達到執行不同功能
	* 直接分拆成多個方法，解析留給外部
7. 保持物件完整
	* 某物件被讀取多個屬性
	* 不如整個物件被讀取
8. 將參數替換成方法
	* A()->B()->C()
	* 不如 A( B( C() ) )
	* 函式簡潔
9. 引入參數物件
	* 眾多的參數包成值物件
10. 移除set
	* 建構時設定就不會在動的屬性
11. 隱藏方法
	* 不被外部用->private
12. 將建構函數替換為工廠方法
	* 工廠透過擴充決定new、clone、singleton
13. 封裝向下轉型
	* 竟量將轉型封裝在函式內部
	* 避免客戶程式完成
14. 將錯誤碼替換為拋出例外
	* 與其回傳魔鬼數字或字串不如拋例外
	* 可讀性
15. 將例外代碼替換為測試代碼
	* 用判斷語句避免例外發生

## 6. 有關繼承處理
1. 上移欄位
	* 欄位移到父類
2. 上移方法
3. 上移建構函數
4. 下移方法
5. 下移欄位
6. 抽取子類別
	* 擴展功能
7. 抽取父類別
8. 抽取介面
9. 摺疊繼承體
	* 抽取父類別反向程序
	* 父子類別差異過小
	* 降低程式廣度或深度
10. 塑造樣板函數
	* SOP化
	* 抽取父類執行順序
	* 實作子類完成不同需求
11. 將繼承替換為代理
	* 解決繼承氾濫、多繼承的方法
	* 取消繼承改用屬性變數參考到原本父類別
	* 額外寫函式執行原本父類public和protected方法
12. 將代理替換為繼承
	* 將繼承替換為代理反向程序
	* 代理會造成方法過多

# 結語

### 面對變化：兩頂帽子
### 開發
* X 大設計
* O **小設計 -> 測試 -> 整個系統 -> 重構**
#### 延伸
* 敏捷開發
* 測試驅動設計
* 持續整合
* 往復式開發