2008年11月20日 星期四

極品程式碼--refactoring踢到鐵板(Part 1)

最近遇到一個老舊的.Net系統,一直不斷出問題,而且程式碼複雜到大家都不敢碰。恰巧這陣子在練習refactoring的功力,所以手癢之下,決定拿其中最常惹麻煩的一個功能來翻修。

哇!這隻程式真是可怕。簡直像是經過Obfuscated的程式。正常的coding有可能做出這麼難下手的程式嗎?不知道當時原作者怎麼能想出這麼難懂的程式。只能用極品來形容。真要去追蹤只會頭暈目眩,像是天龍八部的珍瓏棋局一樣,內力不夠的話去盯著它看會不會吐血走火入魔呀!

究竟有多可怕呢?下面舉例講述其中一個字串變數strSysInfo生命週期,一個坎苛多變的命運。

strSysInfo從第322行開始代表從頁面某個欄位擷取的值。從第874行起strSysInfo是一個SQL Select結果的某個欄位,從1354行的if條件中的for迴圈中,strSysInfo會被用來接select query的字串,為了WebService回傳用,我實在不確定那個if幾時會被執行到,因為條件複雜到不行。奇特的是,1600行開始用strSysInfo的值當作資料庫update的條件。請注意,到這裡她的身份不只改變了多次,而且前一次變身還是在if中的迴圈,如果有進入if,那麼她現在是的身份是迴圈中最後一筆,如果沒進入if那她還是先前的身份。現在到了1600行居然不問她的身份,直接讓她當update條件!

這樣的程式碼行為可能是對的嗎?還是抓到了bug? 完全沒概念

介紹完strSysInfo如油麻菜籽,令人辛酸的一生,回到這個method。其實像strSysInfo這樣,在method中會多次改變用途的變數有近百個,一起在method的開頭就全數宣告。如果全都介紹的話,可能會寫成一大本水滸傳,108條好漢的故事。

refactoring有個很重要的特性是,並不需要看懂程式碼才能翻。反過來,像是能夠在黑暗中靠觸覺摸索施工,一步步地翻,到某一步會忽然豁然開朗︰終於了解這支程式大致在做些什麼了!了解了以後,refactoring就能進行得更快更順手。所以有句話說,"Refactoring is an active form of code inspection."翻它是為了看懂它,而不該等到看懂了才敢翻。平時讀程式的同時就可以順手refactor。

雖然Refactoring有許多高檔的技巧,各種Design Patterns的巧妙應用,不過我個人認為,程式碼還是要先符合基本的結構化或是模組化的要求,才能進一步考慮用物件技術去優化它。所以最重要的兩個技巧,還是rename與extract method。有太多程式是連最基本的架構都做得很差。

我以前總認為extract method如果抽出的是void MyNewMethod()這樣沒有輸入輸出的method,是沒什麼幫助的,只是把程式碼切成小塊而已。總是認為︰一個一千行的程式,把他切成五份兩百行的程式,有什麼用呢?至少也應該要突顯出資料傳遞的關係,才是有意義的動作。最近常處理一些狀況很差的程式碼以後,完全推翻了過去的想法。因為有的程式,你光要把他分解成兩半就是個很困難的動作。即使只將一個method切成兩個,往往也是一個關鍵性的進步了。你面對的是兩千行的程式,還是兩百行的程式,這當然差很多!

像這次遇到的極品,我居然摸了一整天,僅止於rename variable以及改善排版,完全找不到著力點做extract method。一個都沒有!不管框哪一個段落,都有一堆糾雜的變數。通常我們extract method會讓有參考到而不改變值的變數當input,會改變值的變數當output。但是現在我似乎不管擷取哪一段,都會有一些變數既是要讀取值,也自己會改變值。勉強extract method只會弄得更亂更難懂。

第一次動搖自己對refactoring這個方法的信心︰會不會有的程式根本無可救藥,是沒辦法refactored的?我一邊發呆,一邊本能地做些微小的動作如改名字,移除一些明顯用不到的垃圾等等,一方面在懷疑究竟是自己的問題,還是真的很難翻。也想著該不該放棄,直接建議重新開發。後來想起Michael Feathers在"Working Effectively with Legacy Code"書中說的一個經驗︰為了能入手,一開始的翻修不一定能讓程式簡化或是變好懂,有時反而是弄得更醜,這是必經的過程。想到這裡,決定放棄任何美學的直覺以及個人的尊嚴,放手下去做了,extract出超醜超複雜無人能懂的method,把整個程式弄得更亂更噁心。即使是段譽來看也會吐血了吧。哇哈哈,不管了,這下怎麼樣都無所謂了。(自暴自棄中)

卻怎麼樣也沒想到,這個動作就像是虛竹亂下的那一著,真的破了珍瓏棋局。 ---請期待part 2

看官如果有什麼棘手的程式碼的經驗,可否簡單的留個言,交流一下呢?

4 則留言:

丁丁 提到...

聽起來超有趣~ 期待 part 2!

喵尾巴 提到...

no pains no gains .....真是辛苦了...^^;

匿名 提到...

^^ 呵~~你寫的文章真的深度!! 期待+1

EC 提到...

呵呵,謝謝留言呀。請多指教。