You are on page 1of 11

什麼是重構(Refactoring)?

王文忠

作者 Martin Fowler 的解釋
Refactoring is the process of changing a software system in such
a way that it does not alter the external behavior of the code yet
improves its internal structure. It is a disciplined way to clean up
code that minimizes the chances of introducing bugs.
… improving the design of the code after it has bean written.
重構是在不改變程式碼外在行為的前提下,對程式碼做出修改,以改進程
式的內部結構。重構是一種有紀律的程式整理方法,可以將整理過程中引入錯
誤(bug)的機會降低…在程式碼寫好之後改進它的設計。

什麼是「程式碼外在行為」?
如果就一個 method 來說,他的外在行為就是他所被定義的
Input/Output,也就是參數(Parameters)與回傳值(Return Value),如果就
一個 class 而言,他的外在行為就是他的 Public Methods,所以說,
Refactoring 是在於外界沒有感覺的狀況下,提昇程式碼內在的品質。

Domain Layer 與重構
我想,在說明下去之前,先從下面這張圖開始,自從上次讀書會之後,大
家應該比較了解 Domain Layer 中,有三種主要的 Pattern:Transaction
Script, Domain Model 與 Table Module。 Transaction Script 一開始所要
花費的力氣比較小,但在邏輯複雜度不斷增加之下,會越來越難以維護。相對
的,Domain Model 雖然一開始所要花費的力氣較多,但是在未來面臨邏輯
複雜度加增的衝擊之下,仍然是可以掌控與管理的。

Transaction
Script

Effort to
Enhanc
e

Table Module

可掌握

Domain
Model

花同樣的力氣, 可以管理比較複雜的邏輯
比較簡單
Complexity of Domain Logic

只是,這三個 Pattern 之間就這麼個自獨立、不相往來嗎?答案是否定的,
因為一旦我們把 Refactoring 加進來,就會出現第四條線。
Transaction
Script

Effort to
Enhance

Table Module

Domain Model

Refactoring

Complexity of Domain Logic

事實上,而且通常,我們一開始寫程式都是從最簡單的 Transaction
Script 開始的,但是,不幸的、不得不接受的是程式會一改再改,而且複雜度
也相對地跟著增加,漸漸地,我們就會發現自己的程式越來越難以維護了,更
要命的是程式的品質也不斷地下降,最後,自己也會心虛地覺得「無法掌控自
己的程式」,這時就是程式碼就已經「腐敗」掉了,這樣還能不聞到壞味道嗎?
那麼廁所臭臭的我們會怎麼處理?

Transaction
Script


Effort to
Enhance

Change

Complexity of Domain Logic

當然就是把他清一清、整理整理,這麼一來就可以一改 Transaction
Script 的命運。
Transaction
Script

Table Module

Effort to
Enhance

Change
Refactoring
Complexity of Domain Logic

但是天下沒有白吃的午餐,這次重構的力氣一定比直接寫程式來的多,問
題是多多少?我的經驗裏是「邏輯越複雜,所需要花費的力氣就越大」 ,也
就是說「如果生病了,最好早一點看醫生」,而且現在不做,以後也要做,現
在不是我做,以後別人也要做,所以就會常常聽到有人說:「這個程式我看不
下去了!」 --- 這包括我自己 ^^,然後後面接一句「我想要重寫!」,然後

就看到 PM 在冒汗,如果一支程式是完全不能使用的,我才會考慮重寫,否則
我會把他整理整理,也就是重構。
另一個問題是重構的時間點在什麼時候?通常是遇到變更的時候,也就是
要動手改程式之前。

重構的第一式 --- Extract Method
Step 1 :
我們把 JBuilder 打開,把我們要整理的 Code 選起來,按右鍵選
Refactoring,就可以選擇 Extract Method 的功能。

Step 2 : 輸入要新增的 method 的名稱,按 OK 就可以完成了。

在階層中上下移動的整理功夫 1 – Pull Up Method
在做交易的初期,我們可能會用 Transaction Script 的方式寫交易,然後
開始會整理出一些有用的 methods。
Transaction1
doSomeThing()

當交易越做越多,我們會發現一些共通的地方。
Transaction1

Transaction2

Transaction3

doSomeThing()

doSomeThing()

doSomeThing()

這時,如果我們用繼承的方式來處理共同的 method 時,就會成為以下的
結構。
SuperTransaction
doSomeThing()

Transaction1

Transaction2

Transaction3

在階層中上下移動的整理功夫 2 – Push Down Method
另一方面,當我們在階層架構中有某一些 methods 是只被某些子類別所
用到,做於一些比較特別的事,像這樣屬於做特別的事的 methods ,我們會
把他降下來(Push Down)。

SuperTransaction
(from case1)

doSomeThing()
doSomeThingSpecial()

Transaction2

Transaction1

(from case1)

(from case1)

SuperTransaction
(from case1)

doSomeThing()

Transaction1
(from case1)

doSomeThingSpecial()

Transaction2
(from case1)

責任歸屬的整理功夫 --- Class 對 Class, Move Method
我們在寫程式的時候,通常會有 一個 class 呼叫 另一個 class 的狀況,
當我們有一天發現,這個 class 所擁有的 method (或責任),應該是屬於另一
個 class 時,我們會移動方法(Move Method)。
Class1

Class2

doSomeThing()

Class1

Class2
doSomeThing()

責任歸屬的整理功夫 --- Package 對 Package, Move Class
如果某個 Package 裏的 Class 應該屬於另一個 Package 時,就可以用
移動類別(Move Class)。

Package1

Package2

Package2

Package1

ClassA

ClassD
ClassC

ClassB
ClassE

Package2

Package1

ClassA

ClassC

ClassD

ClassB
ClassE

結總
很簡單地說明什麼是 Refactoring 以及提供自己的一些觀點與例子,相關
的例子在 JBuidler 裏面都有相對應的功能。雖然說程式碼會一改在改,但是在
經驗裏,他會在一次次地修改與整理的功夫下,漸漸地收歛,也會發現自己的
程式在一次次地磨練下慢慢地茁壯起來,自己對程式的掌握越來越清楚,這是
最有成就感的地方。
當然這並不是漫無止境地整理下去,這通常是走入 Try & Error 的學習方
式,這並不是說不好,而是這樣的學習方式是有限的,以下提供兩個建議:
1. Refactoring 到怎麼樣才是好?可以以 Pattern 為一個參考。
2. 以 Open Source Project 為學習對象。
我們遇到的問題,其實大多數別人已經有了解答,我們需要的是找到一個
好的學習對象,平常我們在使用 Open Source 的 Products 時,大多只是學
習怎麼用他(Ex: Struts),而乎略了他內在的機制與設計原理,比如說:
Pattern 與 Pattern 之間是怎麼串連與應用的?怎麼串連與應用才是適當的?
其實答案都在裏面!這也是這次 OOSIG 4 的主題之一!
以上啦哩啦雜地說了一堆,希望可以抛磚引玉,也對大家有一些幫助,感
謝。