在撰寫程式時,快而髒(Quick and dirty)也是種技巧,在快速地建立功能之後,可藉由重構來構築出程式應有之樣貌,既然是種技巧,就不單純只是隨意地撰寫程式,而是有著適當原則與經驗累積。

以快而髒作為開始

身為開發者,自我精進的目標之一是追求無暇的程式碼(Clean code),為此開發者會閱讀大量程式碼、探索各種模式、掌握各種開發原則、致力於彰顯程式本身的語意、符合語言技術等各方面的慣例;相對來說,快而髒地撰寫程式,經常是不被鼓勵的作法。

不過,在一開始就對程式碼作過多考量也是個問題,沒拿捏好,就會發生為了物件導向而物件導向、為模式而模式、想用lambda而lambda等過度設計的情況。

因此,就結果而言,應用程式中確實不應留有快而髒的程式碼,然而,作為功能原型的建構,適當地以快而髒的程式碼作為開始,特別是面對程式碼最終樣貌難以掌握的情況,其實,這會是個有用的技巧!

若是有開發者高聲支持快而髒的程式碼,必然會引來許多開發者的抨擊,因為「稍後會來整理程式碼」的這類計畫,往往礙於各種現實因素而難以實現,這使得快而髒的程式碼作為開始也成了結果,最後不斷地在應用程式中累積成了技術債,對應用程式的維護形成重大傷害。

因此,想要以快而髒作為開始,最重要的原則就是:「別讓它成為結果」,開發者應該在分支中進行,在後續程式碼整理的計畫真正落實之前,都不應該合併至應用程式之中,而且,任何理由都不行(如果打破此原則,那麼,問題就不在快而髒,而在於管理了)。

所以,有些開發者支持此原則的變化形式,若是面對用過就丟的程式碼,例如展示用的原型、生命週期短的應用程式等,才可以使用快而髒的手法來撰寫程式。

另一方面,有經驗開發者快而髒下撰寫的程式碼,相對於沒有經驗的開發者來說,往往已經潔淨許多,也就是說,如同對設計原則、典範、模式等的理解與掌握,會因開發者而有所不同,快而髒的手法也有其技巧,需要經驗累積,而這些都影響了後續整理程式碼時的難易度。

在快而髒中突顯重複

程式碼之所以無暇,要素之一是沒有過多的重複;想在程式碼一開始實行快而髒,快速地的實現出功能確實是主要目的,然而謹記另一個原則「令程式碼中的重複易於察覺」。

基本上,我們若要察覺重複的初級方式,是對於複製、貼上的這個動作有警覺,但是,這還不夠,如果開發者撰寫了一大片程式碼,之後複製至它處,就算察覺了這個動作,對於後續程式碼的整理,多半也沒太大幫助。

最好的方式就是「令任務單純」,快而髒並不是只有指程式碼層面,在打算實現的功能層面上,也要先快而髒。

說穿了,就是大任務分解為小任務,這看似有點矛盾,如果原本就是易於分解任務的話,程式碼又怎麼會快而髒呢?因為這邊說的小任務,指的是不通用的小任務!

最近一次,我用上快而髒手法的例子是px_spiral_text(https://bit.ly/2lRtqos),這是一個由內而外的方塊螺旋(圖可參考https://bit.ly/2kFo7sb),最初我只畫了中心方塊,這就是個小任務,接著直接用程式碼畫出第二個、第三個...每個方塊都是個小任務,相關的值都在程式碼中直接寫死,然而因為方塊的任務夠小,複製、貼上程式碼的動作頻繁了,重複也就顯而易見,自然而然,我們就能察覺了繪製的規律。

想令任務單純,也可以從程式碼層面下手,我只要撰寫的程式碼超過5行,就會想著這邊可否在程式碼排版上「分段」,並「註解」說明該段目的,此時,自然就須思考此時的小任務是什麼;在下段程式碼開始時要用到的變數值,會將之指定給新變數以「避免共用變數」,目的是避免傳遞狀態,如果變數值可更動又共用相同變數,相距甚遠的兩段程式碼,不小心就會考慮了彼此的變數狀態,這樣的程式碼會構成邏輯泥塊,難以察覺重複,就真的蠻骯髒了。

既然如此,就「令變數值不可變動」,如果是物件,就令「物件狀態不可變」,在這樣的情況下,開發者不得不分解出小而簡短的程式片段,儘管程式碼看來可能快而髒,然而因為程式片段都是小而簡短,很容易察覺重複,對於後續的整理會有很大的幫助。

針對快而髒的重構

在快而髒的過程中,只要是對突顯重複有幫助的手法,其實都可以用上,目的就是在步入程式碼整理階段時,易於抽取重複,一開始並不需要用上複雜的重構手法,只要先單純地小片段的程式碼「抽取為函式」,並根據先前的註解為函式「約略命名」就可以了。

在抽出成函式的過程,變數必須將值傳遞給函式參數,成為函式本體的程式碼會被參數化,而每個片段都抽取成函式的結果,就是多了一堆函式定義,由於函式本體夠短而且又參數化了,多個函式本體間的重複就顯而易見,這時,可以試著找出最通用的函式,取代其他的函式呼叫,在這個通用化的過程中經常會發現,被抽取出的函式本體中,還可以再抽取出更小的函式,讓程式碼朝著已經潔淨的方向前進。

程式碼會快而髒,除了大量的重複之外,有個經常被忽略的點就是:副作用與無副作用的片段夾雜。如果面對一大片程式碼,想將有無副作用的程式碼的分離,不是件容易的事,然而,若是面對夠短的函式就會簡單許多,開發者應該試著「將無副作用的程式片段從函式中分離出來」,具有副作用的函式純粹以呼叫的方式,來取用被分離的無副作用函式。

當快而髒的程式碼演變為具有大量(小)函式的階段,這時可以開始套用語言技術或典範了,支援物件導向的語言就試著將相關功能組織為物件,套用物件導向相關的重構手法,具有lambda特性的語言可以試著用lambda,傳遞程式碼片段來令既有的函式更具彈性等;下一步就是符合程式的規範,無論是符合社群慣例或是團隊規定等,行有餘力,就是朝無暇程式碼的方向邁進了。

從快而髒與重構中累積經驗

每個開發者都曾快而髒地寫過程式,想必也難以否認以它作為開始的好處,只是在軟體開發流程中,往往因為時程等因素,使得快而髒之後的重構沒有落實,從而令其蒙上惡名;然而單從程式設計的角度來說,快而髒是個技巧,若搜尋「quick and dirty code」,也可以看到支持快而髒或為其平反的討論。

如同重構需要練習、熟悉與琢磨,快而髒的技巧也是,因開發者的經驗不同,眼中曾經是無暇的程式碼,假以時日回顧,也可能是快而髒,掌握原則,從快而髒與重構中累積足夠經驗後,自身眼中的快而髒,在某些開發者眼中,或許也有一定的潔淨程度了!

專欄作者

熱門新聞

Advertisement