論Groovy的動態特徵

作者/宋定遠

前言 身為一個軟體工程師,有很多時候會想寫一些小程式來驗證自己的想法、或者把一些常常需要做的例行工作自動化,但有時自己所熟悉的程式語言不論是開發流程或者在語法設計上都太過嚴謹了,有時還沒開始實作就覺得工作似乎又變更多,因此"開發自己的工具"這個念頭有很高的機率會胎死腹中。 雖然如此,但每當要去做那些明知可以自動化的工作時,仍然會想到自己想開發一個工具的初衷,畢竟當初會有這樣的念頭就意味著不喜歡重覆性太高的工作,只是礙於不知道這東西要開發多久、在時間的評估上就無法說服自己去投入這件事。為了擺脫這種〝被時代所限制“的困境,筆者曾經做了一些不同方面的研究,但都因為一些原因而覺得不滿意,整理如下(假設最熟悉的程式語言是Java為例):
  • 在不同的功能上使用被廣為推薦的Library感覺是最穩紮穩打的,但姑且不論熟悉這些Library要花多久時間,最根本的問題-開發速度較慢仍然沒有得到解決。這裡的慢並不是單純指從開發到驗證的步驟較多,而是程式碼對於想法改變所造成的程式結構調整的因應能力比較差。
  • 針對某些部份的功能使用Shell Script來完成,其實就效果上來說是比較符合我的想像,容易上手而且驗證較快,但因為似乎沒辦法直接使用Java Library的關係把這個主題先擱置在一旁了,畢竟不想浪費一開始為熟悉各Library所做的努力。
  • 使用ANT(Another Neat Tool)來支援處理流程,就擴充性來說或許是最完整的,而且也很模組化,但將處理流程獨立於XML檔案所得到的好處實在是太少了(因為其實XML本身也很囉嗦),而且最重要的資料處理流程依然是用傳統的方式寫在類別之中,沒有真正加速到什麼地方,否則ANT其實是還能接受的一個方案。
所有的這些努力都是為了同一個目標,即希望程式碼的表達能力能夠提高、並且避免重覆。最後,筆者的研究目標來到動態語言,並發現它或許就是我所追求的答案,而選擇Groovy的原因則是因為有個Web Application Framework稱為Grails是架構於它之上,而Web Application是筆者下一步的研究目標。 Groovy的動態特徵 動態語言的宗旨是〝優化人的時間、而不是機器的“,這在硬體不斷貶值的現實下讓動態語言的存在更有道理,也因為此宗旨,動態語言的原生設計支援很多種數據結構、能將對於數據的操控融入在語法之中,這大大增加程式碼的可讀性、並同時減少程式碼的行數,看起來就像把資料處理邏輯用較簡潔的語言來表示,將來維護或優化時能從中得到好處。在擁護動態語言優於靜態語言的理由之中,最有趣的是從測試的觀點來切入,因為現在的系統都蠻龐大的,為了確保功能的正確性的確需要單元測試甚至整合測試,而反正都要測試,那麼〝動態語言的錯誤都要等到執行時期才能發現“這個特徵似乎也不那麼像是個缺點了,而且動態語言在測試上似乎還比較方便。 在所有Groovy所提供的特徵中我選出了三個在純Java環境中較少著墨的來介紹,並藉此來說明動態語言在開發上的方便性:
    一、Closure Closure是一個函式物件,可以做為函式或另一個Closure的參數傳入,在效果上就像為一個函式動態地附加一段程式碼。Closure並不是Groovy特有的新東西,JavaScript早就在使用此特徵了。 二、Metaprogramming Metaprogramming的概念簡單來說就是將變數引入程式的執行流程中,使各個階段會被觸發的函式、產生的參數都是變動的,例如下面這個Groovy的例子: 在網路上有人說這稱做〝用程式碼產生程式碼“,我想這在直觀上的確可以說明Metaprogramming是怎麼回事。 除此之外,Groovy在每個物件中還附加了一個可以新增函式、資料、甚至建構子的Metaclass-ExpandoMetaClass,而這使Groovy在支援Metaprogramming上成為一個非常強大的語言。 三、AST(Abstract Syntax Tree) Transformation 在執行Groovy的程式碼時其實還是需要經過編譯的(畢竟它是跑到JVM上),所謂的Abstract Syntax Tree是指編譯過程中用來表示程式碼所定義的類別及描述組合邏輯的物件,而AST Transformation則是指根據我們在程式碼中的標記,在這個物件中安插進特定的程式碼,使動態語言程式碼的高可讀性得以保存,也節省了一些打字的時間,所以在Groovy的官網上又稱之為Compile-time Metaprogramming。
以上三個特徵是從Java開始對Groovy開始感到好奇時要花最多時間了解的部份,而Closure雖然上面寫得很清楚簡單,但實際上因為Groovy提供了太多的Syntax Sugar,使Closure在程式碼中出現的位置很靈活、Variable Scope也不像Java那麼固定,甚至不小心還會造成Syntax Error,我想這是所有Groovy初學者都要經歷的陣痛期,包括筆者也還是在練習。 結語 根據以前動態語言的使用者經驗,聽說動態語言的執行速率是遠遜於需要編譯的靜態語言(如Java),並且上面提到的Groovy特徵似乎會使得系統的Object Graph變得更混亂,進而使得物件導向分析設計該遵守的原則有被侵犯的疑慮,例如開閉原則(OCP, Open-Close Principle)和單一責任原則(SRP, Single-Responsibility Principle)。如此看來,為了保有〝使用動態語言能降低之後維護的負擔"的特徵,雖然動態語言雖然提供許多方便的語法,但似乎也不表示能完全無視設計上的原則。 因此,筆者認為最適合使用動態語言的情景是要快速驗證結果、或進行原型開發時使用,當有了原型之後就能跟客戶端談談〝他們真正想要到底是什麼“,這在需求收集階段是非常合理而且有效的;需求收集當中,此原型可得利於動態語言容易因應改變的特性而能夠較快速地回應客戶的新需求,而需求收集完成之後,可將此原型的邏輯以靜態語言實作以得到預期中的速率提昇,並藉此時機再次檢視各類別的設計,這才是最穩健的開發方法。 參考資料