Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/cvxvgjca/public_html/www.kenming.idv.tw/wp-content/plugins/wp-syntax/wp-syntax.php on line 380
** 所有相關活動的完整照片,可至-「2013.05.18_Clean Code 讀書會」Flickr 相片集瀏覽。 **
上上星期六 (5/18) HSDc. 所舉辦的「Clean Code (無瑕的程式碼) 讀書會」,在相當熱絡的研討與歡樂的氣氛下,圓滿落幕了。
「Clean Code」中譯本的技術編輯(Simon, 博碩文化)也特別蒞臨此次的活動,與會過程中不只用心地回應讀者許多問題外,還分享了在翻譯該書時的用字遣詞,以盡能表達出原文的意境,實在相當感謝他能為業界翻譯如此高品質的譯本。還有,他也為讀友們帶來一項福利,在博碩的粉絲團內按個「讚」,就有機會抽中「Clean Code」原版書籍,定價可是 NT$2,200,不便宜呢。
這次讀書會的導讀與主持不得已就由我個人來擔任。我把時間安排大致分為讀書內容的心得分享、問題提問、自由討論。
一開始先作個腦力激盪 (brain storm),這裡我用心智圖 (mindmap)先整理出關於「Clean Code」的 5W-What, Why, When, Which, Who。雖然看來僅是簡單的整理,但讀友們大都發言踴躍,在分享一些對「乾淨的程式碼」的看法與心得,以及一些問題討論,這樣也討論到中午 12:40 才休息,大家各自用完中餐後,下午 1:30 繼續研討。
下午整個主題就完全以書本大綱內的「How-to」為重心了,一章一章地討論大綱內容。畢竟作者就已開宗明義內提及:「小事情上誠實,可不是一件小事。」;同時又提到:「神 (我以為是魔鬼?)就在細節內。」。所以,程式碼當然就要回歸到「How-to」細節內,這可是不能眼高手低的。
事實上,這本書大概到第 10 章就足夠了,後續的內容,有其他作者撰寫的、有作者早期的論文,也有從「Refactoring」一書節錄的重構型錄,許多內容其實翻過就可以了。(不過看來我最認真!? 關於第14章-「持續地精煉」,我可是逐行閱讀其程式碼,整整花了三個晚上還不容易了解內容,直至與 Ringle 討論過後,他認為該範例還是不夠「Clean」,仍需要繼續「精煉」。 >_<)
即使只研讀到第10章左右,但也不容易,直到下午約5:00結束時才勉強「翻完」,其實仍是有諸多細節尚未討論的。不過也算是盡心了,讀友們的發言夠是踴躍了,也相信有許多研討的內容對未來他們的工作,在長期上會有相當體會與助益的。
這次讀書會仍是在吳興街的「加爾第咖啡」舉辦的。因為這裡交通也算便利,地下1F可容納近30人的會議空間,還可以擺上投影機簡報,另外他們家的手工單品咖啡,可是相當甘醇美味的。
我們請店家準備了兩筒-咖啡與紅茶供無限暢飲。這咖啡可是店內服務生一壺壺煮出來單品曼巴,外面這樣一杯起碼要百元以上。下午讀友們喝完後還再請店內加煮一筒,但就喝不完啦,我看每位讀友平均有喝上三杯以上之多,過量啦。
同時也準備了一些小點心餅乾,就當作是下午茶點。
對了,我也請 Ringle 展示一下書本第10章內的範例程式碼。我是一直認為重構僅從程式碼解析,那是只有大師級的才可以輕而易舉做到,若是一般平凡如我輩者,最好能佐以「設計圖」的視覺化方式來解讀,相信就會輕鬆許多的。
對於要解讀他人的程式碼,最重要的可不是「類別圖 (class diagram)」,反而是凸顯物件互動的「循序圖 (sequence diagram)」。所以早在兩年餘前,HSDc. 就已開發出「sequnce generator」產品,可以掃描程式碼,並產出 UML 循序圖,可以參考原整理的共四篇文-程式碼與 UML 類別_循序圖 的關係探討-完結 (4)。這次剛好可以派上用場,就利用自家的工具產品展示如何解析該範例程式碼。
書內已重構後的程式碼參考如下,可以比對上列循序圖的 method call 呼叫情形。
/** * This class Generates prime numbers up to a user specified * maximum. The algorithm used is the Sieve of Eratosthenes. * Given an array of integers starting at 2: * Find the first uncrossed integer, and cross out all its * multiples. Repeat until there are no more multiples * in the array. */ public class PrimeGenerator { private static boolean[] crossedOut; private static int[] result; public static int[] generatePrimes(int maxValue) { if (maxValue < 2) return new int[0]; else { uncrossIntegersUpTo(maxValue); crossOutMultiples(); putUncrossedIntegersIntoResult(); return result; } } private static void uncrossIntegersUpTo(int maxValue) { crossedOut = new boolean[maxValue + 1]; for (int i = 2; i < crossedOut.length; i++) crossedOut[i] = false; } private static void crossOutMultiples() { int limit = determineIterationLimit(); for (int i = 2; i <= limit; i++) if (notCrossed(i)) crossOutMultiplesOf(i); } private static int determineIterationLimit() { // Every multiple in the array has a prime factor that // is less than or equal to the root of the array size, // so we don't have to cross out multiples of numbers // larger than that root. double iterationLimit = Math.sqrt(crossedOut.length); return (int) iterationLimit; } private static void crossOutMultiplesOf(int i) { for (int multiple = 2*i; multiple < crossedOut.length; multiple += i) crossedOut[multiple] = true; } private static boolean notCrossed(int i) { return crossedOut[i] == false; } private static void putUncrossedIntegersIntoResult() { result = new int[numberOfUncrossedIntegers()]; for (int j = 0, i = 2; i < crossedOut.length; i++) if (notCrossed(i)) result[j++] = i; } private static int numberOfUncrossedIntegers() { int count = 0; for (int i = 2; i < crossedOut.length; i++) if (notCrossed(i)) count++; return count; } } |