說了那么多理論,我們來看看怎樣使用抽取方法來重構遺留系統。如前所述,重構的過程首先是閱讀程序代碼,邊閱讀邊整理程序。將功能相對獨立的代碼段放在一起,在前面加上注釋。調整一些程序的順序,將相關的代碼盡量放在一起,但要保證程序執行的結果不會發生改變。比較典型的,將變量的定義與使用變量的代碼放在一起。這個步驟比較實用,因為許多的遺留系統,其代碼都有一個壞毛病,就是在程序開始時定義一大堆變量,但要弄清這些變量都用來做什么,卻十分困難。邊讀邊調整,將變量的定義逐漸遷移到使用它的代碼段中,將大大提高代碼可讀性,你甚至會發現并簡化一些變量。
前面的工作為抽取函數做好了準備,但你不必閱讀和整理完所有的代碼才開始抽取。許多遺留函數的大函數非常長,你可以整理一部分,就開始著手重構。比如,把剛才分段的代碼段抽取出來,形成一個獨立的函數。在這里,代碼段與注釋,是我們決定是否需要抽取成函數的重要標志之一。
在閱讀過程中,許多長相相似的代碼也是我們需要重視的重要代碼。重復代碼是許多遺留系統代碼質量差的重要原因之一,因此提高代碼復用就成為了代碼優化一個重要的項目。整合大量重復的代碼,將其提取到一個統一的函數中為其它各處所調用,是一個值得推薦的辦法。因此,重復代碼也是抽取函數的重要標志。
除此之外,一些塊操作的語句,如條件語句、循環語句、try語句,都可能成為抽取函數的標志。最典型的就是if語句,包含在if語句中間的常常是一個相對獨立的功能,譬如一次重構中,原代碼長得像這樣:
整個這段代碼有數千行之多,但整體結果就是用這樣的一系列if語句組成。隨后,我將每個if語句中的代碼都提取出來形成了各自的函數。它們被重構成這樣:
這樣,原來if語句中的業務操作代碼,就被抽取到chkCard(), chkIc(), chkBuffer()這樣的函數中了。起初我們將if語句中的所有代碼都抽取出來寫入這些函數中,這是十分自然而然想到的辦法。但隨后發現這樣需要將response作為參數傳遞給這些函數中,這樣的設計不太好。因此,將代碼還原回來重新重構,將寫入response的操作寫入到servletOutput()中了。整個過程如圖5.1所示:
完成了此次重構以后,我們原來這個超級大函數由數千行代碼,縮減到了百來行代碼,這是一個可喜的進步,函數變得結構清晰而易于閱讀。但是,被抽取出來的新函數卻依然龐大,它們有的會達到一千多行,閱讀依然困難。這時我們運用“抽取方法”繼續分解。比如這個chkCard(),它執行的是一大堆校驗,每個校驗其功能都相對獨立。因此,我首先調整代碼順序,將每個校驗的代碼都獨立成一段,在前面添加相應的注釋。然后使用抽取方法,將校驗抽取到一個一個函數中。
整個數千行代碼的超級大函數,就這樣原子裂變式地逐漸分解,最后分解成數十個函數。每個函數只有數十行代碼,并通過注釋標注它們的用途與參數、返回值含義。這樣,一個起初難于閱讀的函數,經過一系列重構,開始變得可以閱讀了。是的,我們開始邁出了可喜地一步。但一個對象包含了數十個方法,這些方法被凌亂地堆砌在一起,沒有層級、沒有主次。最關鍵是,雖然每個方法都不大,但這個對象卻包含數千行代碼,依然顯得臃腫。因此我們還需要后面的步驟繼續重構。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
前面的工作為抽取函數做好了準備,但你不必閱讀和整理完所有的代碼才開始抽取。許多遺留函數的大函數非常長,你可以整理一部分,就開始著手重構。比如,把剛才分段的代碼段抽取出來,形成一個獨立的函數。在這里,代碼段與注釋,是我們決定是否需要抽取成函數的重要標志之一。
在閱讀過程中,許多長相相似的代碼也是我們需要重視的重要代碼。重復代碼是許多遺留系統代碼質量差的重要原因之一,因此提高代碼復用就成為了代碼優化一個重要的項目。整合大量重復的代碼,將其提取到一個統一的函數中為其它各處所調用,是一個值得推薦的辦法。因此,重復代碼也是抽取函數的重要標志。
除此之外,一些塊操作的語句,如條件語句、循環語句、try語句,都可能成為抽取函數的標志。最典型的就是if語句,包含在if語句中間的常常是一個相對獨立的功能,譬如一次重構中,原代碼長得像這樣:
...... if (cmd != null && cmd.equals("chkCard")){ //此處省略了500行 } else if (cmd != null && cmd.equals("chkIc")){ //此處省略了300行 } else if (cmd != null && cmd.equals("chkBuffer")){ //此處省略了1000行 } ......
整個這段代碼有數千行之多,但整體結果就是用這樣的一系列if語句組成。隨后,我將每個if語句中的代碼都提取出來形成了各自的函數。它們被重構成這樣:
...... if (cmd != null && cmd.equals("chkCard")){ byte[] ret = chkCard(reader); servletOutput(res, ret); } else if (cmd != null && cmd.equals("chkIc")){ byte[] ret = chkIc(reader); servletOutput(res, ret); } else if (cmd != null && cmd.equals("chkBuffer")){ byte[] ret = chkBuffer(reader); servletOutput(res, ret); } ......
這樣,原來if語句中的業務操作代碼,就被抽取到chkCard(), chkIc(), chkBuffer()這樣的函數中了。起初我們將if語句中的所有代碼都抽取出來寫入這些函數中,這是十分自然而然想到的辦法。但隨后發現這樣需要將response作為參數傳遞給這些函數中,這樣的設計不太好。因此,將代碼還原回來重新重構,將寫入response的操作寫入到servletOutput()中了。整個過程如圖5.1所示:
圖5.1分解大函數的示例
完成了此次重構以后,我們原來這個超級大函數由數千行代碼,縮減到了百來行代碼,這是一個可喜的進步,函數變得結構清晰而易于閱讀。但是,被抽取出來的新函數卻依然龐大,它們有的會達到一千多行,閱讀依然困難。這時我們運用“抽取方法”繼續分解。比如這個chkCard(),它執行的是一大堆校驗,每個校驗其功能都相對獨立。因此,我首先調整代碼順序,將每個校驗的代碼都獨立成一段,在前面添加相應的注釋。然后使用抽取方法,將校驗抽取到一個一個函數中。
整個數千行代碼的超級大函數,就這樣原子裂變式地逐漸分解,最后分解成數十個函數。每個函數只有數十行代碼,并通過注釋標注它們的用途與參數、返回值含義。這樣,一個起初難于閱讀的函數,經過一系列重構,開始變得可以閱讀了。是的,我們開始邁出了可喜地一步。但一個對象包含了數十個方法,這些方法被凌亂地堆砌在一起,沒有層級、沒有主次。最關鍵是,雖然每個方法都不大,但這個對象卻包含數千行代碼,依然顯得臃腫。因此我們還需要后面的步驟繼續重構。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
