May 15 22:17~23:30
前幾天Teddy介紹了「The Art of Readable Code」這本書,有熱心的鄉民馬上就推薦了The Practice of Programming、Code Complete、Clean Code、Code Craft、The Pragmatic Programmer、Beautiful Code、Working Effectively with Legacy Code這幾本。今天Teddy又擠不出什麼料,只好拿出老招,介紹一下書本的內容。今天要介紹的是Teddy的偶像Kent Beck所寫的Implementation Patterns這本書的第70-72頁所介紹的Guard Clause這個implementation patterns。
鄉民們可以把這本書想像成跟GoF的Design Patterns屬於類似性質的書,只不過GoF的書介紹了23個設計層面的樣式,而Kent Beck這本書談的是屬於實作(程式設計)方面的樣式。學會Implementation Patterns也可以讓程式設計師寫出容易閱讀的程式碼。
Implementation Patterns這本書很薄,只有一百五十幾頁,但是算一算應該有介紹一百個左右的patterns。這些patterns都很小,小到讓你會懷疑,這樣也算一個「pattern」喔。總之,Kent Beck說這是patterns,那就是嘍。
言歸正傳,回到今天的主題 Guard Clause,這個中文要怎麼翻?姑且稱作「看守子句」好了…Orz。看一下書中的例子:
void initializeA() {
if (!isInitialized()) {
…
}
}
void initializeB() {
if(isInitialized())
return();
…
}
上面這兩種寫法,鄉民們認為哪一種比較好?請先花六十秒想一下。
Kent Beck建議採用initializeB()的寫法是比較好的,因為
if(isInitialized())
return();
這樣的寫法就稱為Guard Clause。為什麼要叫做Guard Clause?因為這個if clause(if子句)扮演著「看守」或是說「警衛」的角色。只要這個Guard Clause成立,程式就不會繼續執行下去(做壞事被警衛抓到)。所以讀到initializeB()前兩行程式時,便可知道這個initializeB()可以被呼叫很多次,但是只有第一次會實際執行初始化的動作。
至於第一種寫法的if,代表著一個if-then-else表示式。根據Kent Beck的說法,因為if-then-else表示式裡面的if子句和else子句都是相等重要的控制流程,看到if (!isInitialized()) 的時候,他的腦袋會提醒他等一下要記得去看相對應的else子句的內容。在這個例子裡面當然不需要else子句,所以這種情況使用if-then-else表示式就比較不好。
但是,實際上很多程式設計師會採用第一種寫法。為什麼?因為傳統的程式設計課程告訴鄉民們,一個副程式應該只有一個進入點與一個離開點,所以如果用第二種寫法,initializeB() 就有兩個不同的離開點,以傳統的程式設計思維來看,反而是不好的寫法。Kent Beck認為,傳統的建議是適用在像是FORTRAN或是組合語言這種使用到大量全域變數的程式中。但是對於近代的語言,像是Java,在一個小小的method裡面,大部分存取的都是區域變數。如果還是堅守「一個副程式應該只有一個進入點與一個離開點」,就顯得有點過於保守。
再看一個書中的例子,這也是Teddy寫程式經常會遇到的情況:
void computeA() {
Server server = getServer();
if (server != null) {
Client client = server.getClient();
if (client != null) {
Request current = client.getRequest();
if (current != null)
processRequest(current);
}
}
}
void computeB() {
Server server = getServer();
if (server == null)
return;
Client client = server.getClient();
if (client == null)
return;
Request current = client.getRequest();
if (current == null)
return;
processRequest(current);
}
哪一種寫法比較清楚且容易閱讀?Teddy很久以前是比較傾向會寫出computeA()這種寫法,但是學了Guard Clause之後,當然是要改用computeB()這種寫法嘍。
最後再看一個書中的例子:
while (line = reader.readline()) {
if (line.startWith(‘#’) || line.isEmpty())
continue;
// Normal processing logic
}
其中
if (line.startWith(‘#’) || line.isEmpty())
continue;
也是Guard Clause。
***
友藏內心獨白:又是一把攜帶方便,簡單又實用的小扁鑽。
沒有留言:
張貼留言