重構—改善既有程式的設計(chapter 2,3)

35
重重 - 重重重重重重重重重 重重 重重 Chris Huang

Upload: chris-huang

Post on 15-Dec-2014

1.066 views

Category:

Education


3 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 重構—改善既有程式的設計(chapter 2,3)

重構 - 改善既有程式的設計第二、三章

Chris Huang

Page 2: 重構—改善既有程式的設計(chapter 2,3)

第二章 重構原則

Page 3: 重構—改善既有程式的設計(chapter 2,3)

何謂重構 名詞:對軟體內部的一種調整,目的在不

改變『可察的行為』前提下,提高可理解性,降低修改成本

動詞:使用一系列重構準則,在不改變『可察的行為』前提下,調整其結構

兩頂帽子添加新功能:不應修改既有程式碼重構:不能在添加功能

Page 4: 重構—改善既有程式的設計(chapter 2,3)

為何重構 改進軟體設計

只為短期目的而修改程式,程式碼會逐漸腐敗 使軟體更易被理解

你的程式碼還有第二位讀者(可能就是自己) 幫助找臭蟲

可深入瞭解程式行位,幫助除錯 幫助提高編程速度

惡劣的設計很快就會讓開發速度慢下來

Page 5: 重構—改善既有程式的設計(chapter 2,3)

何時重構 三次法則

事不過三,三則重構 添加功能時

重構幫助理解方便添加功能方便以後添加功能

修補錯誤時重構幫助理解方便除錯

復審程式碼時幫助審閱別人的程式碼(我比較持保留態度)

Page 6: 重構—改善既有程式的設計(chapter 2,3)

怎麼跟經理說 經理懂技術:設法證明給他看

把重構當成 code review 意見引入程式碼內 經理不懂:不要告訴他!

只要能快速創造出高效軟體就好

Page 7: 重構—改善既有程式的設計(chapter 2,3)

重構的難題 資料庫

多數商用程式都與 DB schema 緊密結合DB schema 的改變讓人不得不遷移所有資料,可能是

漫長而繁瑣的工作 修改介面

Public interface○ 連同所有用到的程式一起改

Published interface○ 維護兩套介面,讓舊介面使用新介面的函式○ 舊介面加註 deprecate

不要太早發佈介面不要揭露不必要的介面

Page 8: 重構—改善既有程式的設計(chapter 2,3)

重構的難題 難以完成的設計改動

很難把『 no security 』重構成『 good security 』 何時不該重構

實在太亂,重寫比較快 現有程式碼不太能正常運作 方案:把大塊頭軟體封裝良好的組件

○ 除一針對個別組件決定重構 or 重建 專案已瀕臨最後期限

○ 沒時間了,先解決燃眉之急吧

Page 9: 重構—改善既有程式的設計(chapter 2,3)

重構與設計 預先設計聽來很棒,但確保預先設計完全無誤的壓力太大

重構可成為預先設計的替代品,先照最初想法讓程式有效運作,然後不斷重構得到良好的設計

一開始不需要找出完全正確的解決方案,只要足夠合理的解決方案即可

重構讓日後的修改成本不會太高

Page 10: 重構—改善既有程式的設計(chapter 2,3)

重構與效率 不要浪費無謂的時間

隨時關注效能往往沒有效果 90% 的時間花費在 10% 的程式上

Profiling find hot spot tunning 重構雖然會讓程式變慢,但良好的結構反而讓最佳化容易進行 Write tunable software and then tune it

Page 11: 重構—改善既有程式的設計(chapter 2,3)

重構起源何處 (自己看書吧)

Page 12: 重構—改善既有程式的設計(chapter 2,3)

第三章 程式碼的壞味道決定何時重構、何時停止,跟知道如何重構一樣重要

Page 13: 重構—改善既有程式的設計(chapter 2,3)

Duplicated code 用 extract 取出共同的程式碼 Extract push up 推入 superclass 或許可用 from template 函式使用不同演算法,可考慮 substitute algorithm

Page 14: 重構—改善既有程式的設計(chapter 2,3)

Long method 感覺需要寫註解把要講得東西寫成函式,以其用途(非實作)命名 99% extract

大量參數或暫時變數 replace temp with query 尋找註解:程式碼用途和實現手法的語意距離 條件式或迴圈

if(connected) { reportStatus();}

foreach(String host: hosts) { shutdownAgent(host);}

Page 15: 重構—改善既有程式的設計(chapter 2,3)

Large class Instance 變數太多 duplicated code Extract class Extract subclass Extract interface

Page 16: 重構—改善既有程式的設計(chapter 2,3)

Long parameter list 太長的參數列難以理解 函式需要的參數多半可以在 host class 中找到 傳遞物件給函式 Replace parameter with method Preserve whole object Introduce parameter object

void clearCanvas(bytes [] buffer, int width, int height) {}

void clearCanvas(Bitmap bmp) {}

Page 17: 重構—改善既有程式的設計(chapter 2,3)

Divergent change 改 feature 1

動 function A, B, E, F 改 feature 2

動 function B, C, D, K (通常這種 class 也會很肥大) 運用 extract class 分拆

Page 18: 重構—改善既有程式的設計(chapter 2,3)

Shotgun surgery

牽一髮,動全身 改 feature 1

動 class A, B, E, F 改 feature 2

動 class B, C, D, K

Move method/field Inline class

Page 19: 重構—改善既有程式的設計(chapter 2,3)

Feature envy Move method Extract method 反例

Strategy, visitor, self delegation 為了對抗 divergent change

class A {

B b;

void func1() { b.init(); if(b.getConnected()) { info(b.getStatus()); } else { b.alert(); } }

void func2() { while(b.read(buffer)) { b.signal(); } }}

Page 20: 重構—改善既有程式的設計(chapter 2,3)

Data clumps 刪定一組中的一個,其他就變得

沒有意意 (通常也有 duplication ) Extract class Introduce parameter object Preserve whole object 改了後可能產生 feature envy 繼續重構

class A { ... string schema; string host; int port; ...}

Class B { ... string schema; string host; int port; ...}

Page 21: 重構—改善既有程式的設計(chapter 2,3)

Primitive obsession 不愛用小物件,拼命用 primitive

Ex: Money, Range, Date, PhoneNumber, ZipCode Replace data with object Replace type code with class Replace type code with subclass Replace type code with state/strategy …

Page 22: 重構—改善既有程式的設計(chapter 2,3)

Switch statements 通常也有 duplication 改一個,統統都得改 可用多型解決

void connect(int host_type) { ... switch(host_type) { case 1: ((mysql) handle).connect(); break; case 2: ((mssql) handle).connect(); break; }}

void disconnect(int host_type) { ... switch(host_type) { case 1: ((mysql) handle).disconnect(); break; case 2: ((mssql) handle).disconnect(); break; }}

Page 23: 重構—改善既有程式的設計(chapter 2,3)

Parallel inheritance 對某個 class 新增 subclass, 也必須為另一個新增 兩個繼承體系的 subclass 名稱自首相同 Refer to Move method, move field

Page 24: 重構—改善既有程式的設計(chapter 2,3)

Lazy class Class 經過修改後,重要性已縮水 勇敢砍吧 Collapse hierarchy Inline class

Page 25: 重構—改善既有程式的設計(chapter 2,3)

Speculative class 『總有一天會需要』而留的程式 把他搬掉

等用到再說吧 Collapse hierarchy Remove parameter Rename method

Page 26: 重構—改善既有程式的設計(chapter 2,3)

Temporary field 暫時變數都放 instance 暫時變數只為某些函式而用,只是為了不想傳參數 Extract method Extract class

Page 27: 重構—改善既有程式的設計(chapter 2,3)

Message chains 客戶端將搜尋過程中的

structure of navigation 緊密耦合,修改物件關係將導致客戶端也需修改

string A::getInfo() { return b.getInfo();}

String B::getInfo() { return c.getInfo();}

String C::getInfo() { return d.getInfo();}

String D::getInfo() { return e.getInfo();}

Page 28: 重構—改善既有程式的設計(chapter 2,3)

Middle man 過度使用 delegation Remove middle man Replace delegation with

ingeritance

class A { B b; int func1() { return b.func1(); }

int func2() { return b.func2(); }

int func3() { return b.func3(); }}

Page 29: 重構—改善既有程式的設計(chapter 2,3)

Inappropriate intimacy 兩個 class 過份親密,互探對方

private Move method Move field Change bidirectional

association to unidirectional Extract class Hide delegate

class A { friend class B; ...}

Class B { friend class A; ...}

Page 30: 重構—改善既有程式的設計(chapter 2,3)

Alternative classes with different interface

Rename method 反覆運用 move method 直到 protocol 一致 或 extract superclass

Page 31: 重構—改善既有程式的設計(chapter 2,3)

Incomplete library class 3rd-part library 無法更動 Introduce foreign method Introduce local extension

Page 32: 重構—改善既有程式的設計(chapter 2,3)

Data class 只有 field 跟一堆 getXXX, setXXX Encapsulate field Encapsulate collection Remove setting method

Move method 把呼叫行為搬進 data class Hide method 把 public 變成 private

Page 33: 重構—改善既有程式的設計(chapter 2,3)

Defused bequest 避開某些特定繼承 十之八九壞味道很淡,可以忽略 不能 reuse superclass 的實作(拒絕實作),卻又不支 superclass 的介面

Replace inheritance with delegation

Page 34: 重構—改善既有程式的設計(chapter 2,3)

Comments 把 comments 當除臭劑用 程式有很長的註解這裡的程式寫得很糟糕 當你感覺需要寫註解,請先試著重構,試著讓註解

變得多餘 Extract method rename method Introduce assertion

如果你不知道該做什麼,才是寫註解的好時機

Page 35: 重構—改善既有程式的設計(chapter 2,3)

Q&A