# 重構-向模式前進<br/>Refactoring to Patterns ## 1. 為什麼寫這本書 ### 1.1 過度設計(Over-Engineering) ### 1.2 模式萬靈丹(The Patterns Panacea) ### 1.3 設計不足(Under-Engineering) ### 1.4 測試驅動開發和持續重構(Test-Driven Development and Continuous Refactoring) ### 1.5 重構與模式(Refactoring and Patterns) ### 1.6 漸進式設計(Evolutionary Design) --- ## 2. 重構(Refactoring) ### 2.1 何謂重構(What Is Refactoring?) ### 2.2 什麼促使我們重構(What Motivates Us to Refactor?) ### 2.3 眾目睽睽(Many Eyes) ### 2.4 人類易讀的程式碼(Human-Readable Code) ### 2.5 保持程式碼簡潔(Keeping It Clean) ### 2.6 小步驟(Small Steps) ### 2.7 設計債(Design Debt) Ward Cunningham 對設計債隱喻為財務。 沒有堅持做以下三件事,就會招致設計債: * 移除重複碼。 * 簡化程式碼。 * 讓程式碼目的更明確。 ### 2.8 演變出一個新體系架構(Evolving a New Architecture) 漸進式設計(evolutionary design)提供更好的方法 * 成立一個團隊。 * 以應用層(appliction layer)的需求來設計框架層(framework layer)。 * 持續利用重構來改善應用層和框架層。 ### 2.9 複式重構與測試驅動式重構(Composite and Test-Driven Refactorings) ### 2.10 複式重構的好處(The Benefits of Composite Refactorings) 每一個複式重構都對準特定的某個 pattern,並有下列好處: * 它們描述重構次序的完整規劃(an overall plan for a refactoring sequence)。 * 它們對於並非顯而易見的設計方向帶來啟發。 * 它們對 pattern 的實作提供深刻洞見。 ### 2.11 重構工具(Refactoring Tools) --- ## 3. 模式(Patterns) ### 3.1 什麼是模式(What Is a Pattern?) * 物件導向設計(object-oriented design) * 物件導向分析(object-oriented analysis) * 領域設計(domain design) * 程序和組織設計(process and organizational design) * 使者者介面設計(user interface design) Alexander 每個 pattern 都是三部分組成的規則(three part rule): * 描述情境(context)。 * 問題(problem)。 * 解決方案(solution)。 ### 3.2 模式帶來的喜樂(Patterns Happy) ### 3.3 條條大路通模式(There Are Many Ways to Implement a Pattern) ### 3.4 重構成為、接近、遠離模式(Refactoring to, towards, and away from Patterns) |Pattern|To (成為)| Towards (接近) |Away (遠離)| | -------- | -------- | -------- |-------- | | Adapter | Extract Adapter<br/>Unify Interfaces with Adapter |Unify Interfaces with Adapter|| |Builder|Encapsulate Composite with Builder||| |Collecting Parameter|Move Accumulation to Collecting Parameter||| |Command|Replace Conditional Dispatcher with Command |Replace Conditional Dispatcher with Command || |Composed Method|Composed Method||| |Composite|Replace One/Many Distinctions with Composite<br/>Extract Composite<br/>Replace Implicit Tree with Composite||Encapsulate Composite with Builder| |Creation Method|Replace Constructors with Creation Methods||| |Decorator|Move Embellishment to Decorator|Move Embellishment to Decorator|| |Factory|Move Creation Knowledge to Factory<br/>Encapsulate Classes with Factory||| |Factory Method|Introduce Polymorphic Creation with Factory Method||| |Interpreter|Replace Implicit Language with Interpreter||| |Iterator|||Move Accumulation to Visitor| |Null Object|Introduce Null Object||| |Observer|Replace Hard-Coded Notifications with Observer|Replace Hard-Coded Notifications with Observer|| |Singleton|Limit Instantiation with Singleton||Inline Singleton| |State|Replace State-Altering Conditionals with State|Replace State-Altering Conditionals with State|| |Strategy|Replace Conditional Logic with Strategy|Replace Conditional Logic with Strategy|| |Template Method|Form Template Method||| |Visitor|Move Accumulation to Visitor|Move Accumulation to Visitor|| ### 3.5 模式會讓程式碼更複雜嗎?(Do Patterns Make Code More Complex?) ### 3.6 模式知識(Pattern Knowledge) ### 3.7 使用模式進行預先設計(Up-Front Design with Patterns) --- ## 4. 程式碼壞味道(Code Smells) |Smell|Refactoring| | -------- | -------- | |Duplicated Code|Form Template Method<br/>Introduce Polymorphic Creation with Factory Method<br/>Chain Constructors<br/>Replace One/Many Distinctions with Composite<br/>Extract Composite<br/>Unify Interfaces with Adapter<br/>Introduce Null Object| |Long Method|Compose Method<br/>Move Accumulation to Collecting Parameter <br/>Replace Conditional Dispatcher with Command <br/>Move Accumulation to Visitor<br/>Replace Conditional Logic with Strategy| |Conditional Complexity|Replace Conditional Logic with Strategy <br/>Move Embellishment to Decorator<br/>Replace State-Altering Conditionals with State<br/>Introduce Null Object| |Primitive Obsession|Replace Type Code with Class<br/>Replace State-Altering Conditionals with State<br/>Replace Conditional Logic with Strategy<br/>Replace Implicit Tree with Composite<br/>Replace Implicit Language with Interpreter<br/>Move Embellishment to Decorator<br/>Encapsulate Composite with Builder| |Indecent Exposure|Encapsulate Classes with Factory| |Solution Sprawl|Move Creation Knowledge to Factory| |Alternative Classes with Different Interfaces|Unify Interfaces with Adapter| |Lazy Class|Inline Singleton| |Large Class|Replace Conditional Dispatcher with Command <br/>Replace State-Altering Conditionals with State<br/>Replace Implicit Language with Interpreter| |Switch Statements|Replace Conditional Dispatcher with Command<br/>Move Accumulation to Visitor| |Combinatorial Explosion|Replace Implicit Language with Interpreter| |Oddball Solution|Unify Interfaces with Adapter| ### 4.1 重複的程式碼(Duplicated Code) ### 4.2 過長的函式(Long Method) ### 4.3 複雜的條件式(Conditional Complexity) ### 4.4 基本偏執(Primitive Obsession) ### 4.5 不當暴露(Indecent Exposure) ### 4.6 解法蔓生(Solution Sprawl) ### 4.7 貌異實同的類別(Alternative Classeswit hDifferent Interfaces) ### 4.8 冗員類別(Lazy Class) ### 4.9 過大的類(Large Class) ### 4.10 Switch 語句(Switch Statement) ### 4.11 組合爆炸(Combinatorial Explosion) ### 4.12 詭譎方案(Oddball Solution) --- ## 5. 一份 Refactorings to Patterns 名錄(A Catalog of Refactorings to Patterns) ### 5.1 重構的記錄形式(Format of the Refactorings) ### 5.2 本目錄中引用的項目(Projects Referenced in This Catalog) #### 5.2.1 XMLBuilder #### 5.2.2 HTMLParser #### 5.2.3 貸款風險計算器 ### 5.3 起點(A Starting Point) ### 5.4 學習順序(A Study Sequence) |Session|Refactoring| | -------- |-------- | |1|Replace Constructors with Creation Methods<br/>Chain Constructors| |2|Encapsulate Classes with Factory| |3|Introduce Polymorphic Creation with Factory Method| |4|Replace Conditional Logic with Strategy| |5|Form Template Method| |6|Compose Method| |7|Replace Implicit Tree with Composite| |8|Encapsulate Composite with Builder| |9|Move Accumulation to Collecting Parameter| |10|Extract Composite<br/>Replace One/Many Distinctions with Composite| |11|Replace Conditional Dispatcher with Command| |12|Extract Adapter<br/>Unify Interfaces with Adapter| |13|Replace Type Code with Class| |14|Replace State-Altering Conditionals with State| |15|Introduce Null Object| |16|Inline Singleton<br/>Limit Instantiation with Singleton| |17|Replace Hard-Coded Notifications with Observer| |18|Move Embellishment to Decorator<br/>Unify Interfaces<br/>Extract Parameter| |19|Move Creation Knowledge to Factory| |20|Move Accumulation to Visitor| |21|Replace Implicit Language with Interpreter | --- ## 6. 創建(Creation) ### 6.1 Replace Constructors with Creation Methods<br/>以創建函式(Creation Methods)替代建構式(Contructors) 開發過程中為數眾多的 classes 建構式令客戶難以決定呼叫哪一個建構式。 以 目的清楚、返回物件實體 的 Creation Method 取代建構式。 ![](https://i.imgur.com/YxrFqJu.png) #### 6.1.1 動機 當某些語言要求每個建構式都必須以其 class 名稱命名。 如果有多個建構式,客戶端必須選擇呼叫哪一個建構式,為此得研究參數,並在建構式源碼中摸索。 建構式無法有效傳遞目的。容易誤用某個建構式。當程式員必須選擇呼叫那個建構式時,會降低開發速度。 #### 6.1.2 做法 #### 6.1.3 示例 Loan.class 貸款風險計算器(loan risk calculator) Loan class 有 7 種貸款(loan),這裡只討論其中三種。 * loan(貸款) * revolver(循環信用貸款) * revolving credit term loan(循環信用定期貸款) ``` public class Loan{ public Loan(double commitment, int riskRating, Date maturity) { this(commitment, 0.00, riskRating, maturity, null); } public Loan(double commitment, int riskRating, Date maturity, Date expiry) { this(commitment, 0.00, riskRating, maturity, expiry); } public Loan(double commitment, double outstanding, int customerRating, Date maturity, Date expiry) { this(null, commitment, outstanding, customerRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, int riskRating, Date maturity, Date expiry) { this(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date maturity, Date expiry) { this.commitment = commitment; this.outstanding = outstanding; this.riskRating = riskRating; this.maturity = maturity; this.expiry = expiry; this.capitalStrategy = capitalStrategy; if (capitalStrategy == null) { if (expiry == null) this.capitalStrategy = new CapitalStrategyTermLoan(); else if (maturity == null) this.capitalStrategy = new CapitalStrategyRevolver(); else this.capitalStrategy = new CapitalStrategyRCTL(); } } ``` ##### Step 1 找出單元測試 ``` public class CapitalCalculationTests public void testTermLoanNoPayments() { Loan termLoan = new Loan(commitment, riskRating, maturity); } ``` ##### Step 2 依照意圖(Intent)命名並建立新的函式 ``` public class CapitalCalculationTests... public void testTermLoanNoPayments() { Loan termLoan = createTermLoan(commitment, riskRating, maturity); } public static Loan createTermLoan(double commitment, int riskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); } ``` ##### Step 3 將 createTermLoan 函式移到 Loan class。 將 CapitalCalculationTest class 中的 createTermLoan 函式刪除。 Loan.class ``` public class Loan{} public static Loan createTermLoan(double commitment, int riskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); } ``` CapitalCalculationTest.class ``` public class CapitalCalculationTest{ public void testTermLoanNoPayments() { ... Loan termLoan = Loan.createTermLoan(commitment, riskRating, maturity); ... } ``` ##### 目標 ![](https://i.imgur.com/L0fh2Gx.png) 為了達成上圖目標我試著還原完整的 Loan class Loan.class ``` /** * @author RainBowT * Loan.class * 貸款風險計算器(loan risk calculator) * Loan class 有 7 種貸款(loan),這裡只討論其中三種。 * 1. loan(貸款) * 2. revolver(循環信用貸款) * 3. revolving credit term loan(循環信用定期貸款) */ public class Loan { private double commitment; private double outstanding; private int riskRating; private Date maturity; private Date expiry; private CapitalStrategy capitalStrategy; public Loan(double commitment, int riskRating, Date maturity) { this(commitment, 0.00, riskRating, maturity, null); } public Loan(double commitment, double outstanding, int riskRating, Date expiry) { } public Loan(double commitment, int riskRating, Date maturity, Date expiry) { this(commitment, 0.00, riskRating, maturity, expiry); } public Loan(double commitment, double outstanding, int customerRating, Date maturity, Date expiry) { this(null, commitment, outstanding, customerRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, int riskRating, Date maturity, Date expiry) { this(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date expiry) { } public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date maturity, Date expiry) { this.commitment = commitment; this.outstanding = outstanding; this.riskRating = riskRating; this.maturity = maturity; this.expiry = expiry; this.capitalStrategy = capitalStrategy; if (capitalStrategy == null) { if (expiry == null) { this.capitalStrategy = new CapitalStrategyTermLoan(); } else if (maturity == null) { this.capitalStrategy = new CapitalStrategyRevolver(); } else { this.capitalStrategy = new CapitalStrategyRCTL(); } } } } ``` 根據上 Loan class 可以發現根本搞不清楚哪種貸款要使用哪個建構式。 因此利用建立 public static 函式並利用建構式意圖命名函式能幫助開發人員選擇對應的建構式。 完整 Loan class ``` /** * @author RainBowT * Loan.class * 貸款風險計算器(loan risk calculator) * Loan class 有 7 種貸款(loan),這裡只討論其中三種。 * 1. loan(貸款) * 2. revolver(循環信用貸款) * 3. revolving credit term loan(循環信用定期貸款) */ public class Loan { private double commitment; private double outstanding; private int riskRating; private Date maturity; private Date expiry; private CapitalStrategy capitalStrategy; public Loan(double commitment, int riskRating, Date maturity) { this(commitment, 0.00, riskRating, maturity, null); } public Loan(double commitment, double outstanding, int riskRating, Date expiry) { } public Loan(double commitment, int riskRating, Date maturity, Date expiry) { this(commitment, 0.00, riskRating, maturity, expiry); } public Loan(double commitment, double outstanding, int customerRating, Date maturity, Date expiry) { this(null, commitment, outstanding, customerRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, int riskRating, Date maturity, Date expiry) { this(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry); } public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date expiry) { } public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date maturity, Date expiry) { this.commitment = commitment; this.outstanding = outstanding; this.riskRating = riskRating; this.maturity = maturity; this.expiry = expiry; this.capitalStrategy = capitalStrategy; if (capitalStrategy == null) { if (expiry == null) { this.capitalStrategy = new CapitalStrategyTermLoan(); } else if (maturity == null) { this.capitalStrategy = new CapitalStrategyRevolver(); } else { this.capitalStrategy = new CapitalStrategyRCTL(); } } } //------------------- Replace Constructors with Creation Methods --------------------// public static Loan createTermLoan(double commitment, int riskRating, Date maturity) { return new Loan(commitment, riskRating, maturity); } public static Loan createTermLoan(CapitalStrategy riskAdjustedCapitalStrategy, double commitment, double outstanding, int riskRating, Date maturity) { return new Loan(riskAdjustedCapitalStrategy, commitment, outstanding, riskRating, maturity, null); } public static Loan createRevolver(double commitment, double outstanding, int riskRating, Date expiry) { return new Loan(commitment, outstanding, riskRating, expiry); } public static Loan crateRevolver(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date expiry) { return new Loan(capitalStrategy, commitment, outstanding, riskRating, expiry); } public static Loan createRCTL(double commitment, double outstanding, int riskRating, Date maturity, Date expiry) { return new Loan(commitment, outstanding, riskRating, maturity, expiry); } public static Loan createRCTL(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date maturity, Date expiry) { return new Loan(capitalStrategy, commitment, outstanding, riskRating, maturity, expiry); } } ``` #### 6.1.4 變體 Extract Factory ![](https://i.imgur.com/rBWMtgb.png) ### 6.2 Move Creation Knowledge to Factory<br/>將創建知識(Creation Knowledge) 移至 Factory 用以具現(instantiate) class 的各種資料和程式碼蔓延於許多 classes。 把創建知識(creation knowledge) 搬移至單個 Factory class 中。 ![](https://i.imgur.com/3rQmajA.png) #### 6.2.1 動機 一旦物件的`創建知識`散於多個 classes 內,便是出現了 Creation Sprawl(創建碼蔓生)壞味道:不該在物件創建過程中扮演任何角色的 classes,其內卻出現創建任務(creational responsibility)。 #### 6.2.2 做法 #### 6.2.3 示例 ### 6.3 Encapsulate Classes with Factory<br/>以 Factory 封裝眾多 Classes 客戶端直接具現 位於同一個 package 內並實作共同 interface 的 classes。 把 class 建構式改設為 non-public 並讓客戶端以 Factory 創建 classes 實體。 ![](https://i.imgur.com/iHJH70g.png) #### 6.3.1 動機 只要客戶端需要知曉`某些 classes 的實體存在全信息(very existence of those classes)`,客戶端直接具現那些 classes 的能力就有價值。但如果客戶不需要那些知識呢? 作法是讓一個 Factory 創建並返回`實作某共同介面`的實體。 #### 6.3.2 做法 #### 6.3.3 示例 AttributeDescriptor.class ``` public abstract class AttributeDescriptor{ protected AttributeDescriptor(){ } } ``` BooleanDescriptor.class ``` public class BooleanDescriptor extends AttributeDescriptor{ public BooleanDescriptor() { super(); } } ``` DefaultDescriptor.class ``` public class DefaultDescriptor extends AttributeDescriptor{ public DefaultDescriptor() { super(); } } ``` ReferenceDescriptor.class ``` public class ReferenceDescriptor extends AttributeDescriptor{ public ReferenceDescriptor() { super(); } } ``` ##### Step 1 確定可以建立以下的實體 ``` protected List createAttributeDescriptors() { List result = new ArrayList(); result.add(new DefaultDescriptor("remoteId", getClass(), Integer.TYPE)); result.add(new DefaultDescriptor("createdDate", getClass(), Date.class)); result.add(new DefaultDescriptor("lastChangedDate", getClass(), Date.class)); result.add(new ReferenceDescriptor("createdBy", getClass(), User.class, RemoteUser.class)); result.add(new ReferenceDescriptor("lastChangedBy", getClass(), User.class, RemoteUser.class)); result.add(new DefaultDescriptor("optimisticLockVersion", getClass(), Integer.TYPE)); return result; } ``` ##### Step 2 實施 Etract Method 產出一個名為 forInteger 的 static creation method ``` protected List createAttributeDescriptors() { List result = new ArrayList(); result.add(forInteger("remoteId", getClass(), Integer.TYPE)); } private DefaultDescriptor forInteger(String remoteId, Class<? extends AttributeDescriptor> aClass, Class<Integer> type) { return new DefaultDescriptor("remoteId", aClass, type); } ``` #### Step 3 目標 ``` protected List newCreateAttributeDescriptors() { List result = new ArrayList(); result.add(forInteger("remoteId", getClass(), Integer.TYPE)); result.add(forDate("createdDate", getClass(), Date.class)); result.add(forDate("lastChangedDate", getClass(), Date.class)); result.add(new ReferenceDescriptor("createdBy", getClass(), User.class, RemoteUser.class)); result.add(new ReferenceDescriptor("lastChangedBy", getClass(), User.class, RemoteUser.class)); result.add(forInteger("optimisticLockVersion", getClass(), Integer.TYPE)); return result; } private DefaultDescriptor forDate(String createdDate, Class<? extends AttributeDescriptor> aClass, Class<Date> dateClass) { return new DefaultDescriptor(createdDate, aClass, dateClass); } private DefaultDescriptor forInteger(String remoteId, Class<? extends AttributeDescriptor> aClass, Class<Integer> type) { return new DefaultDescriptor(remoteId, aClass, type); } ``` #### 6.3.4 變體 ### 6.4 Introduce Polymorphic Creation with Factory Method<br/>以 Factory Method 導入多型創建(Polymorphic Ctration) 繼承體系(hierarchy) 內的 classes 以類似方式實現某函式,其間的唯一差別只在物件創建手段(object creation step)。 為該函示製作單個 superclass 版本,稱為 Factory Method,專門負責處理物件的具現行為(instantiation)。 ![](https://i.imgur.com/5B2VwTC.png) #### 6.4.1 動機 #### 6.4.2 做法 #### 6.4.3 示例 XMLBulider 允許客戶端輕鬆產生 XML。 而後發現還需要至一個 DOMBulider,類似 XMLBuilder。 令 DOMBuilderTest 實例 DOMBulider 而不是 XMLBuilder。 DOMBuilderTest.class ``` public class DOMBuilderTest { private OutputBuilder builder; public void testAddAboveRoot() { String invalidResult = "<orders>" + "<order>" + "</order>" + "</orders>" + "<customer>" + "</customer>"; builder = new DOMBuilder("orders"); // used to be new XMLBuilder("orders") builder.addBelow("order"); try { builder.addAbove("customer"); fail("expecting java.lang.RuntimeException"); } catch (RuntimeException ignored) {} } private void fail(String s) { } } ``` DOMBulider 的一個關鍵設計目標,是讓它和 XMLBuilder 共享相同型別 : OutputBuilder。 ![](https://i.imgur.com/aycDBto.png) ##### step 1 將實例邏輯(instantiation logic)提煉為一個實例函式(instatiation method)。 DOMBuilderTest.class ``` public class DOMBuilderTest extends TestCase... protected OutputBuilder createBuilder(String rootName) { return new DOMBuilder(rootName); } public void testAddAboveRoot() { String invalidResult = "<orders>" + "<order>" + "</order>" + "</orders>" + "<customer>" + "</customer>"; builder = createBuilder("orders"); builder.addBelow("order"); try { builder.addAbove("customer"); fail("expecting java.lang.RuntimeException"); } catch (RuntimeException ignored) {} } ``` ##### step 2 XMLBuilderTest ``` public class XMLBuilderTest extends TestCase... private OutputBuilder createBuilder(String rootName) { return new XMLBuilder(rootName); } public void testAddAboveRoot() { String invalidResult = "<orders>" + "<order>" + "</order>" + "</orders>" + "<customer>" + "</customer>"; builder = createBuilder("orders"); builder.addBelow("order"); try { builder.addAbove("customer"); fail("expecting java.lang.RuntimeException"); } catch (RuntimeException ignored) {} } ``` ##### step 3 Extract Superclass ``` public class AbstractBuilderTest extends TestCase { } public class XMLBuilderTest extends AbstractBuilderTest... public class DOMBuilderTest extends AbstractBuilderTest... ``` ##### step 4 From Template Method ``` public class AbstractBuilderTest extends TestCase { protected OutputBuilder builder; } public class XMLBuilderTest extends AbstractBuilderTest... private OutputBuilder builder; public class DOMBuilderTest extends AbstractBuilderTest... private OutputBuilder builder; ``` abstract Method ``` public abstract class AbstractBuilderTest extends TestCase { protected OutputBuilder builder; protected abstract OutputBuilder createBuilder(String rootName); } ``` pull up method ``` public void testAddAboveRoot() { String invalidResult = "<orders>" + "<order>" + "</order>" + "</orders>" + "<customer>" + "</customer>"; builder = createBuilder("orders"); builder.addBelow("order"); try { builder.addAbove("customer"); fail("expecting java.lang.RuntimeException"); } catch (RuntimeException ignored) {} } ``` ### 6.5 Encapsulate Composite with Builder<br/>以 Builder 封裝複合物(Composite) 建置一個複合物(building a Composite) 讓 Builder 處理所有細節,藉以簡化建置(bulid)。 ![](https://i.imgur.com/ymOHu05.png) ![](https://i.imgur.com/uBwlsdv.png) ![](https://i.imgur.com/r4mkqRQ.png) #### 6.5.1 做法 #### 6.5.2 示例 #### 6.5.3 變體 ### 6.6 Inline Singleton<br/>將 Singleton 內置化 程式碼需要存取物件,卻不需要用來存取該物件的全域點(global point)。 把 Singleton 特徵移至可儲存物件並且提供該物件之存取方法的 class 內,然後刪除 Singleton。 ![](https://i.imgur.com/alIsvkm.png) #### 6.6.1 動機 #### 6.6.2 做法 #### 6.6.3 示例 --- ## 7. 簡化(Simplification) ### 7.1 Compose Method 無法迅速瞭解函式 methods 的邏輯。 把邏輯操作轉換為若干目的清楚且細目等級(detail level)相同的步驟。 ![](https://i.imgur.com/cnvjZvq.png) #### 7.1.1 動機 #### 7.1.2 做法 #### 7.1.3 示例 ### 7.2 Replace Conditional Logic with Strategy<br/>以 Strategy 替代條件邏輯(Conditional Logic) 函式內的條件邏輯控制某一計算工作的各種變異(variants)中的哪一個被執行。 為每個變異(variant)建立起一個 Strategy,然後將函式的計算工作委託(delegate)給 Strategy 實體。 ![](https://i.imgur.com/U6qN1ZV.png) ![](https://i.imgur.com/Q6D6vGe.png) strategy class ![](https://i.imgur.com/5LHe9Jv.png) #### 7.2.1 動機 #### 7.2.2 做法 #### 7.2.3 示例 ### 7.3 Move Embellishment to Decorator<br/>將修飾碼(embellishment code)移至 Decorator 有些程式碼提供修飾功能(provide an embellishment),用來提供 class 的核心任務(core reponsibility)。 將修飾碼(embelishment code)移至一個 Decorator 內。 ![](https://i.imgur.com/JyANaZQ.png) ![](https://i.imgur.com/fyVabgM.png) #### 7.3.1 動機 #### 7.3.2 做法 #### 7.3.3 示例 ### 7.4 Replace State-Altering Conditionals with State<br/>以 State 替代狀態變換語句 控制 物件狀態遷移(object's state transitions) 的條件句十分複雜。 拿用以處理特定狀態及狀態遷移的 state classes 取代條件句。 ![](https://i.imgur.com/oK96cwi.png) ![](https://i.imgur.com/UbEoLfS.png) ![](https://i.imgur.com/wHAsjXV.png) #### 7.4.1 動機 #### 7.4.2 做法 #### 7.4.3 示例 ### 7.5 Replace Implicit Tree with Composite<br/>以 Composite 替代隱寓的樹狀結構 以 Composite 取代原始表述(primitive representation) ![](https://i.imgur.com/jTnSmsz.png) #### 7.5.1 動機 資料或程式碼如果不是很明顯地被建構成樹狀,但卻表現得很樹狀,便是形成一個所謂的 implicit tree。 #### 7.5.2 做法 #### 7.5.3 示例 ### 7.6 Replace Conditional Dispatcher with Command<br/>以 Command 替代條件句派送器。 條件邏輯被用來派送請求(dispatch requestes)和執行行動(execute actions)。 為每個行動(action) 產生一個 Command。把 Commands 儲存在群集(collection)中,並以提取並執行 Commands 的程式碼取代條件邏輯。 ![](https://i.imgur.com/jnDsaud.png) #### 7.6.1 動機 #### 7.6.2 做法 #### 7.6.3 示例 --- ## 8. 一般化(Generalization) ### 8.1 Form Template Method 兩個位於不同 subclasses 內的函式以相同次序執行類似但不完全相同的步驟。 將函式步驟提取至相同簽名式(signatures)的函式中,以此達成函式的一般化。然後上提(pull up)這些一般化函式,形成 Template Method。 ![](https://i.imgur.com/8UhFu9M.png) #### 8.1.1 動機 #### 8.1.2 做法 #### 8.1.3 示例 ### 8.2 Extract Composite 繼承體系中的 subclasses 實作出相同的 Composite。 提煉出一個 superclass,由它來實作 Composite。 ![](https://i.imgur.com/8cXJFDg.png) #### 8.2.1 動機 #### 8.2.2 做法 #### 8.2.3 示例 ### 8.3 Replace One/Many Distinctions with Composite<br/>以 Composite 取代 單/群差異 Class 使用不同的程式碼分別處理單物件和群物件。 運用 Composite 建立起既能處理單物件又能處理群物件的程式碼。 ![](https://i.imgur.com/DHwh9ed.png) #### 8.3.1 動機 #### 8.3.2 做法 #### 8.3.3 示例 ### 8.4 Replace Hard-Coded Notifications with Observer<br/>以 Observer 替代被寫死的通告行為 Subclasses 死板板地(hard-coded)通知另一個 class 單個實體(single instance)。 移除 subclsses,方式是讓它們的 superclass 能夠通知實現 Observer 介面之任何 class 的一或多個實體。 ![](https://i.imgur.com/PmERsVI.png) ![](https://i.imgur.com/BpwHwVq.png) #### 8.4.1 動機 #### 8.4.2 做法 #### 8.4.3 示例 ### 8.5 Unify Interfaces with Adapter<br/>以 Adapter 統一介面。 客戶(Clients)與兩個 classes 互動,以 Adapter 讓介面一致。 ![](https://i.imgur.com/UsrxgRE.png) #### 8.5.1 動機 #### 8.5.2 做法 #### 8.5.3 示例 ### 8.6 Extract Adapter 有個 class 配接(adapts)了某個 Component(組件)或 library (程式庫) 或 API (應用程式介面)或其他 entity(物體)的多重版本。 針對某一版提煉一個對應的 Adapter。 ![](https://i.imgur.com/X4pMIqK.png) #### 8.6.1 動機 #### 8.6.2 做法 #### 8.6.3 示例 #### 8.6.4 變體 ### 8.7 Replace Implicit Language with Interpreter<br/>以 Interpreter(直譯器)取代隱性語言 class 內為數眾多的函式兼備了一個隱性語言元素(elements of an implicit language)。 針對隱性語言的元素定義出 classes,使其實體可被結合構成可直譯語句(interpretable expressions)。 ![](https://i.imgur.com/uqAZpxq.png) #### 8.7.1 動機 #### 8.7.2 做法 #### 8.7.3 示例 --- ## 9. 保護(Protection) ### 9.1 Replace Type Code with Class<br/>以 Class 替代基本型別 某個欄位的型別無法保護自己免受不安全的賦值動作(unsafe assignments)和無價值的相等測試(invalid equality comparisons)。 讓欄位型別成為 class,得以對賦值動作和相等測試設限(constrain)。 ![](https://i.imgur.com/bZV63TN.png) #### 9.1.1 動機 #### 9.1.2 做法 #### 9.1.3 示例 ### 9.2 Limit Instantiation with Singleton<br/>以 Singleton 限制實例次數 程式碼產生多份物體實體,導致耗用太多記憶體或降低系統效能。 以一個 Singleton 取代多份實體。 ![](https://i.imgur.com/13LxSfj.png) #### 9.2.1 動機 #### 9.2.2 做法 #### 9.2.3 示例 ### 9.3 Introduce Null Object<br/>導入無作用物件(Null Object) 處理 null 欄位或 null 變數的邏輯重複出現,遍布整個程式碼。 以 Null Object 取代 null 邏輯。此物件提供適切的 null 行為。 ![](https://i.imgur.com/0vh49oj.png) Null Object 經常透過 subclassing 或實作 interface 的方式實現。 ![](https://i.imgur.com/epwcx4y.png) #### 9.3.1 動機 #### 9.3.2 做法 #### 9.3.3 示例 --- ## 10. 積累(Accumulation) ## 10.1 Move Accumulation to Collecting Parameter<br/>將積累資訊的任務移交給 Collecting Parameter 有一個體積龐大的函式,把資訊積累至一個區域變數(local variable)內。 將結果積累至一個收集資訊用的參數(Collecting Parameter),此參數將被傳給本重新提煉出來的函式。 ![](https://i.imgur.com/SIZ1mEx.png) ### 10.1.1 動機 ### 10.1.2 做法 ### 10.1.3 示例 ## 10.2 Move Accumulation to Visitor<br/>將積累資訊的任務移交給 Visitor 有個函式從異質(heterogeneous)classes中積累資訊。 將積累任務移交給能拜訪每個 class 以積累資訊的 Visitor。 ![](https://i.imgur.com/DRF61ev.png) * 積累函式(accumlation method)負責從異質 classes 積累資訊。 * 外部積累函式(external accumulation method)存在於不屬於異質 class 的某個 class 內。 * 內部積累函式(internal accumulation method)則是存在於異質 classes 的某一個 class 內。 ### 10.2.1 動機 ### 10.2.2 做法 ### 10.2.3 示例 --- ## 11. 工具(Utilities) ### 11.1 Chain Constructors<br/>將建構式(Contructors)鏈結起來 把建構式鏈結起來,以獲得最少量重複碼。 ![](https://i.imgur.com/5KuBzCF.png) #### 11.1.1 動機 #### 11.1.2 做法 #### 11.1.3 示例 ### 11.2 Unify Interfaces<br/>將介面統一起來 需要讓一個 superclass 或 interface 擁有與 subclass 相同的介面。 在 subclass 身上找出 superclass/interface 缺少的所有 public 函式。把這些遺漏的函式加至 superclass 並令它們執行 null 行為。 ![](https://i.imgur.com/BSv8ho5.png) #### 11.2.1 動機 #### 11.2.2 做法 #### 11.2.3 示例 ### 11.3 Extract Parameter<br/>提煉參數 函式或建構式以區域內實例值賦予某個欄位。 賦予此欄位一個由客戶提供的參數,作法是將賦值敘述句的一部分提煉為參數。 ![](https://i.imgur.com/WRlUvmI.png) #### 11.3.1 動機 #### 11.3.2 做法 #### 11.3.3 示例 ## 專業術語 * 類別-責任-合作(Class-Responsibility-Collaboration) * 漸進式設計(evolutionary design) * 複式重構的好處(Composite Refactorings) * 物件導向設計(object-oriented design) * 物件導向分析(object-oriented analysis) * 領域設計(domain design) * 程序和組織設計(process and organizational design) * 使者者介面設計(user interface design) * Regustry Pattern(Patterns of Enterprise Application Architecture) * Type-Safe Enum Pattern * 外部積累函式(external accumulation methods) ## 待瞭解 * FIT 測試框架 * 以模式為引導的重構(pattern-directed refactorings) ## 相關書籍 [Alexander, PL] Alexander, Christopher. A Pattern Language. New York: Oxford University Press, 1977. [Alexander, TWB] Alexander, Christopher. A Timeless Way of Building. New York: Oxford University Press, 1979. [Anderson] Anderson, Bruce. "Null Object." UIUC Patterns Discussion Mailing List (patterns@cs.uiuc.edu), January 1995. [Astels] Astels, David. Test-Driven Development, a Practical Guide. Upper Saddle River, NJ: Prentice Hall, 2003. [Barzun] Barzun, Jacques. Simple and Direct, 4th ed. New York: HarperCollins, 2001. [Beck, SBPP] Beck, Kent. Smalltalk Best Practice Patterns. Upper Saddle River, NJ: Prentice Hall, 1997. [Beck, TDD] Beck, Kent. Test-Driven Development. Boston, MA: Addison-Wesley, 2002. [Beck, XP] Beck, Kent. Extreme Programming Explained. Reading, MA: Addison-Wesley, 1999. [Beck and Gamma] Beck, Kent, and Erich Gamma. JUnit Testing Framework. Available online at http://www.junit.org. See also Erich Gamma and Kent Beck, "JUnit: A Cook's Tour," Java Report, May 1999. [Bloch] Bloch, Joshua. Effective Java. Boston, MA: Addison-Wesley, 2001. [Cunningham] Cunningham, Ward. "Checks: A Pattern Language of Information Integrity." In Pattern Languages of Program Design, eds. James O. Coplien and Douglas C. Schmidt. Reading, MA: Addison-Wesley, 1995. [DP] Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995. [Evans] Evans, Eric. Domain-Driven Design. Boston, MA: Addison-Wesley, 2003. [Foote and Yoder] Foote, Brian, and Joseph Yoder. "Big Ball of Mud." In Pattern Languages of Program Design IV, eds. Neil Harrison, Brian Foote, and Hans Rohnert. Boston, MA: Addison-Wesley, 2000. [F] Fowler, Martin. Refactoring: Improving the Design of Existing Code. Boston, MA: Addison-Wesley, 2000. [Fowler, PEAA] Fowler, Martin. Patterns of Enterprise Application Architecture. Boston, MA: Addison-Wesley, 2003. [Fowler, UD] Fowler, Martin. UML Distilled, 3rd ed. Boston, MA: Addison-Wesley, 2003. [Gamma and Beck] Gamma, Erich, and Kent Beck. Contributing to Eclipse. Boston, MA: Addison-Wesley, 2003. [Kerievsky, PI] Kerievsky, Joshua. "Pools of Insight: A Pattern Language for Study Groups." Available online at http://industriallogic.com/papers/kh.html. [Kerievsky, PXP] Kerievsky, Joshua. "Patterns & XP." In Extreme Programming Examined, eds. Giancarlo Succi and Michele Marchesi. Boston, MA: Addison-Wesley, 2001. [Parnas] Parnas, David. "On the Criteria to Be Used in Decomposing Systems into Modules." Communications of the ACM, 15(2), 1972. [Roberts, Brant, and Johnson] Roberts, Don, John Brant, and Ralph Johnson. "A Refactoring Tool for Smalltalk." Available online at http://st-www.cs.uiuc.edu/~droberts/tapos/TAPOS.htm. [Solomon] Solomon, Maynard. Mozart. New York: HarperCollins, 1995. [Vlissides] Vlissides, John. "C++ Report." April 1998. Available online at http://www.research.ibm.com/designpatterns/pubs/ph-apr98.pdf. [Woolf] Woolf, Bobby. "The Null Object Pattern." In Pattern Languages of Program Design III, eds. Robert C. Martin, Dirk Riehle, and Frank Buschmann. Reading, MA: Addison-Wesley, 1997. ## 參考文獻 https://www.informit.com https://flylib.com https://www.industriallogic.com https://github.com/marcotesta https://aquastripe.github.io/refactoring-to-patterns-notes/ch11/extract-parameter.html#%E7%AF%84%E4%BE%8B ###### tags: `Golden Notes`