TWJUG《重構專題》學習材料導讀

Ching Yi, Chan
6 min readFeb 10, 2022

--

為什麼想做重構練習呢?

因為發現了適合的材料啊!

又是新的一年啊!(2022)

每年的新開始,就會想要有一種 reset 的感覺。先不要追逐最新最潮的技術,回頭盤點一些其實該牢牢握在手上的小工具。( 詸之聲 :這聽起來適合在年底做吧!?)

至於,要選擇什麼樣的知識來討論這讓我苦惱了很久,最終是選擇了「重構」作為主要想面對的主題。因為,過去幾年與一些試著轉職的網友們聊過,也看過了一些人在培訓機構實作的作品。而他們的作品,不少是公開放在網路上的,這些東西都可以成為練習重構的材料。儘管,在少數幾個專案中,並沒有辦法涵蓋重構名錄上的各種問題,但常見的 bad smell 與新手常見實作型式,反倒是更適合用來討論的例子。

我們選用的例子會是在 GitHub 用 資策會 能搜尋到的 Java Web 專案,畢竟這是培訓的大宗。在幾年前開始接觸到這類的「作品」,去年和網友討論他的專題內容時又再看了一下程式,突然發現同樣的結構,同樣的初學者「樣式」反覆出現在不同屆的專題原始碼內。突然意識到,這是一種 legacy code 的世代複製。這是當下能力所及的實作,若是在入行幾年代有所成長,再回頭看看原來的專題,是不是會有不同想法呢?

所以,我們可以想像著在有了經驗後,怎麼去給「當時的自己」改善的建議。用這個出發點,來用網路上公開的專案進行「重構」的練習。我們得強調,是取得材料然後練習重構所需的「觀點」與「技法」。在過程中,我們會試著用不同的「觀點」來點出問題,並練習可以把程式碼變得更好的方法。

學習材料

透過 GitHub 的搜尋功能,加上 Java 語言的過濾條件。我們可以找出需要的材料:

由於這是個 Eclipse 專案,我們配合它使用 Eclipse 匯入查看:

你可以看到專案中有相當多的 package,並且都是呈對的:

這是典型的資策會專案的組織型式。通常 xxx.yyy package 會是由同一個人實作,而 xxx.yyy 內的 Controller 會是一隻 Servlet 與 URL Pattern 的搭配,多數使用傳統的 Deployment Descriptor (也就是 web.xml) 註冊,在少量的 Servlet 內會看以 @WebServlet 註冊的型式。

依先前訪談的經驗,一個小組會有若干人組成。 看似 最沒問題的,大概會是前端頁面的部分。因為,這專題的主要目標是在展現身為 Java Web 應用程式的初級開發者能力所及的範圍,前端部分多為傳統的非前後端分離的實作方式,搭配 jQuery 進行 AJAX 開發,只有特地為了 AJAX 準備的 Request 部分能沾上前後端分離的邊角。剩下的部分,就依頁面或是「模組」來均攤到各組員身上,而這裡的模組就是我們前面看到的一組 xxx.yyy package。

這樣的 Project Layout 只有方便「圈地」,沒有太大實質的意義。因為,多半是用在寫履歷時,描述合作專案中哪些「模組」是由自身實作的,而不用零散地指出哪些 class 是由自身實作的。可是,這樣的圈地並不會起太大的效用,因為如唐伯虎點秋香提到的:

一眼望去都是綠葉是無法吸引人的注意力的。

我會試著整理它,也許會有一些評批,要針對的並不是實作者,因為那就是培訓單位在有限的時間內,配合整個班級能達到的進度去實作出來的「實力公約數」。去 GitHub 上多查不同的組別,有些也是有進入到 Spring Boot 與 JPA 時代的,但多數都還在很初級的階段。在這個初級的階段,反而是在社群推廣中能協助的,畢竟,我們樂於作為新手的墊腳石,即便新手不知道如何求助於人。

目標與限制

我們的目標是利用這些程式,由他的現狀透過 重構設計模式脫離初級開發者後的經驗 來改善它。大致上,我會先做個簡單地限制:

先不要進入框架的時代

例如:

  • 採用 Spring Framework 或 Spring Boot
  • 使用 Hibernate 或 JPA (Java Persistence API)

儘可能使用標準函式庫提供的工具,但輔助一些 Open Source 的 Library 為主。

除此之外,我們會大量鎖定在各種該「提示」的 Bad Smell 上,特別是新手容易寫出的 Duplicated Code ,並且特別容易在各種型式上重複:

  • 重複的程式碼區塊
  • 重複的程式結構
  • 重複的瑣碎實作

重複會造成諸多的維護問題,他必需被適當地收納。而學習這些「收納」的技巧,也不太需要經年累月的累積才行。單純地,看過了、知道了、用上了、學到了的流程罷了。

PS. 其他太微小的 Coding Style 問題並不會有主題的型式去討論,多數情況會去忽略它或是以碎唸的型式出現在文章或影片中,那些是開發者自己需要養成的習慣。

基本問題列管

在這先粗略地列一些顯而易見的問題,但這並不包含全部的問題。可以隨著有興趣一起參與討論的朋友,自行列出可以提出「改善建議」的項目來追加。

Controller (servlet)

  • 重複的 encoding 設定,這其實可以是 Filter 去處理的
  • 使用者認證採用設計不良,採「明碼」存放在資料庫內
  • 肥大的商業邏輯都擠在 Servlet 內,取其中一隻 Servlet 簡化後的結構如下:

相似的結構,重複了整個專案。這每一個 condition 的 block 內都有著不少行數的實作,夾雜了 Business Logic 與 DAO 的操作。

由於缺乏適當的分層,讓程式看起來不好維護,閱讀時的心裡負擔比較大。至少能切出三層來,讓 Business Logic 放到 Service 內去實作,Controller 就純粹負責 Persentation (收參數 -> 委派 service -> 輸出結果):

controller -> service -> dao

更進一步,若能實作一個小巧的 Dispatcher 去收納「海量」Controller 的 Routing 問題,那會讓程式更加簡潔。

Data Access Object

  • 無意義地抽象化,特別是每一個 Dao 都有自己的 interface,分別給 JDBC 實作與 JNDI 實作使用。除了 Connection 來源不同之外,沒必要維護 2 份一樣的 Dao。還會其中一個修改了,另 一個忘了修改。應該抽象化的對象是 ConnectionFactory。(可以由 YouTube 影片 參考比較的結果)
  • 重複而瑣碎的 JDBC API
  • 對於 Connection、Statement 與 ResultSet 的管理,需要更加簡化。而不是各別手擼 try-catch-finally
  • 對於資料狀態的轉移,必需要更加容易。而非人工去呼叫 set 與 get。

結語

上述列出來的問題,是一些顯而易見的問題。同時,也可能是「拿著作品去面試」會被挑出來討論的問題。希望這個系列,能告訴「小時候不懂事」的自己,用成熟的方法去處理它們。

後續,我們會在 TWJUG 線上聚會的時間,逐步抓一點出來討論研究。

--

--