[面試][設計模式]Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?

本文目錄
  1. 大綱
  2. 1. Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?
  3. 1.1 面試官為什麼會問?
  4. 1.2 面試官想從答案確認什麼?
  5. 1.3 筆者提供的簡答
  6. 2. 回答問題所需具備的知識
  7. 2.1 SOLID 原則
  8. 2.2 Design Pattern 是什麼?解決了什麼問題?
  9. 3. 衍伸問題
  10. 3.1 有處理 Legacy Code 的經驗嗎?
  11. 3.2 有用什麼工具讓團隊有一致的 Coding Style
  12. 參考資源

在功能穩定後,你對程式碼還有要求嗎?

「沒有最好的程式,只有更好的程式。」在完成功能後 Code Review 是非常重要的事情;不只看自己的程式碼,也要多觀摩其他人的程式碼,這個動作除了可以優化程式碼品質外,還可以幫助團隊了解彼此的工作進度。

人無完人,很多時候第一時間想到的解法未必是最好的;解決問題的方法不只一種,可能有更簡潔的寫法或是更好的 Pattern 可以套用,只是開發者不具備這塊的知識儲備;此時 Code Review 就是提升團隊程式水平的方式。

找到需要改善的問題後,Refactoring(重構)就是另一個開始;如果為了盡快完成功能而埋頭狂寫,容易導致日後維護以及交接的困難性;專案越大這個問題會越明顯,所以在完成功能後對程式做 Refactoring 是必要的任務,它能提升你日後解決 Bug、需求變更時的工作效率。

大綱

  1. Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?
    • 1.1 面試官為什麼會問?
    • 1.2 面試官想從答案確認什麼?
    • 1.3 筆者提供的簡答
  2. 回答問題所需具備的知識
    • 2.1 SOLID 原則
    • 2.2 Design Pattern 是什麼?解決了什麼問題?
  3. 衍伸問題
    • 3.1 有處理 Legacy Code 的經驗嗎?
    • 3.2 有用什麼工具讓團隊有一致的 Coding Style

1. Code Review 會注意哪些事?會依照什麼原則對程式做 Refactoring?

1.1 面試官為什麼會問?

只要是軟體工程師的職位,這題可以說是超級常見面試題;因為它能問得很淺也能夠問得很深,如果是相對資淺的職位,求職者只要能說出關鍵字並給出基本解釋就能過關;如果是資深的位置,除了關鍵字外,面試官會繼續詢問你在專案中是否有實踐。


1.2 面試官想從答案確認什麼?

  • 瞭解求職者 Code Review 會在意的點
  • 有使用哪些方法對程式做 Refactoring
  • 對 SOLID 原則有基礎認知
  • 知道 Design Pattern 且能舉出實務上的應用
  • 有用過什麼工具讓團隊有一致的 Coding Style

1.3 筆者提供的簡答

我會先觀察變數與函式的命名是否符合團隊規範、變數的宣告是否合理(var/let/const 的使用時機);然後看程式在設計上是否有符合 SOLID 原則;在功能完成後如果發現有合適的 Pattern 可以更好地解決問題,就會再對程式做 Refactoring。


2. 回答問題所需具備的知識

其他章節的知識,可能換一個時空背景就用不到了;但今天這個章節的內容,是每位開發人員都需要將其內化成為本能的。

2.1 SOLID 原則

SOLID 是一個物件導向的設計原則,核心目標是為了讓程式碼更乾淨、易讀、好維護,讓程式朝著 Clean Code(無瑕的程式碼)的方向前進。

  • S(Single Responsibility Principle)單一責任原則
    一次只做好一件事;如果把系統比喻成樂高,那單一責任原則就是樂高的每一塊積木。

  • O(Open-Closed Principle)開放封閉原則
    程式的架構要容易擴充功能;而這個功能的擴充是新增程式,而非修改過去的程式,這麼做可以維持原有程式的穩定性。

  • L(Liskov Substitution Principle)里氏替換原則
    在繼承中衍生的子類別,要能完全繼承父類別的功能;實作時還需顧慮到單一責任原則,避免日後功能拆分或刪減時的困擾。
    以軟體的角度來說,使用者期待版本從 1.0 ➞ 1.1 時行為是一致的,不會更新完舊功能就壞掉。

    子類別 Override 時要注意

    • 不可改變父類別先決條件
      假設籃球比賽的參與者年齡限制在 15~80 歲;裡面可能區分為青年組、中年組、老年組,青年組的年齡「可以是 15~30」;但「不可以是 14~30」。
    • 子類別的後置條件不該被削弱
      籃球的得分有「2」分球、「3」分球,都是數字;如果某個比賽改用「三分球」這個字串紀錄得分就是不可以的。
  • I(Interface Segregation Principles)介面隔離原則
    設計程式時不應該預設使用者一定會使用到介面中的多個功能,我們應該盡量讓介面的功能單一;以後端 API 來舉例,我們不要讓一個 API 可以做好多事情,但做每件事情時只用到其中幾個參數。

  • D(Dependency Inversion Principle)依賴反轉原則
    子類別一定會依賴父類別,這樣單向的關係是乾淨的;如果是雙向的依賴就會導致程式碼邏輯難以追蹤。


2.2 Design Pattern 是什麼?解決了什麼問題?

Pattern 是指在不同場景下的解釋,包含 Context(情境敘述)、Problem(問題)、Solution(解決方案)。

而 Design Pattern 就是過去人們發現解決問題的套路;學習越多 Design Pattern 在遇到問題時可以有更多的思路,並縮短與其他工程師討論的時間,因爲 Pattern 本身就包含了對問題的基礎解決方案,可以省下說明的時間。

下面分享一些常見的 Design Pattern 幫大家建立基礎概念,在看完後讀者也許會恍然大悟,原來我一直都有在使用 Pattern!

  • Factory Method 工廠方法
    簡述:工廠負責生產客戶需要的產品
    假設今天有一個飲料工廠,他會生產很多種類的產品,像是「紅茶、綠茶、烏龍茶」;也許這些飲料的製作過程很複雜,但對客戶來講只要說出自己想要什麼口味的飲料就好了。

  • Strategy 策略
    簡述:一個策略介面底下有很多靈活的方法供選擇
    把計算機的計算當成一個介面,這個介面提供加減乘除的方法;如果想加入次方、開根號的功能也能夠輕鬆擴充。

  • Factory Method 與 Strategy 混合運用
    簡述:有時解決方案是多個 Pattern 的組合
    假設今天開一間飲料店,飲料會分成茶類、咖啡;每杯飲料又有冰量、甜度的選擇,這裡我們可以用 Factory Method 來建立產品;而飲料在販售時,不同組合有對應的折扣,這裡就可以用 Strategy 提供對應的方法來計算總金額。

  • Singleton 單例
    簡述:希望在程式每個位置都能呼叫統一的 Instance
    在寫程式時會希望某些資源可以重複利用,並且在不同檔案引用時這些資料是一致的,像是共用的計時器、資料庫等物件。

  • Decorator 裝飾
    簡述:將需求獨立,依據實際需求取得動態的結果
    假設一間餐廳提供三種餐點

    • 商業午餐(主餐)
    • 簡餐(主餐 + 飲料)
    • 套餐(主餐 + 飲料 + 甜點)

    我們發現上面餐點是有疊加性的,所以可以用繼承的方式來撰寫餐點間的關係;但如果今天老闆想要更換簡餐的內容組合(ex:贈送水果),而不想改變套餐的內容,就會導致程式必須改寫。
    面對這個問題,我們可以用 Decorator 作為解決方案,將主餐、飲料、甜點、水果各自獨立,依據商業午餐、簡餐、套餐實際的內容來做組合;這樣就能減少耦合性的問題。

  • Observer 觀察者
    簡述:觀察者要能掌握被觀察者的狀態改變,較常用於 GUI 的設計
    像是 Vue.js 的 watch 就是用來監聽一個「值」的變化去做一系列的事情(ex:UI 上的調整)。

  • Command 命令
    簡述:將請求的物件和執行的物件分開
    路邊攤的老闆通常是一個人負責備料、點餐、料理、清潔…等到生意做起來有店面了,他才會考慮雇用一些人手來幫忙內場、外場,如果把這個解決方案用 Command 來舉例:

    • 客人(Client):跟服務生說自己想點什麼餐(Concrete Command)。
    • 服務生(Invoker):紀錄客人的餐點(Concrete Command),並呼叫命令。
    • 廚師(Receiver):收到命令後,開始料理餐點(Concrete Command)。
  • Builder 建造者
    簡述:依照自己的需求客製化產品
    一台筆電是由許多零件組裝而成,你可以選擇標配也能夠用選配來客製化它;為了讓選配的零件規格有彈性,Builder 就是一個很棒的解決方案;它讓你先選完自己所需的零件後,再產生最後的產品。

備註:如果你熟悉的 Pattern 剛好能解決目前遇到的問題那是皆大歡喜;萬一沒有合適的 Pattern 可以套用,千萬不要為了設計而設計;開發人員的目標應該是想透過 Pattern 解決問題,而不是從 Pattern 出發去設計問題。


3. 衍伸問題

3.1 有處理 Legacy Code 的經驗嗎?

除非是新創團隊或是接案類型的公司;不然絕大多數新人入職後都要接手前人遺留的程式碼(Legacy Code),如果求職者只有從零開始的經驗,在一開始會有陣痛期。

考點:了解求職者是否接觸過 Legacy Code、碰到時如何快速掌握

之前有因為專案擴編而去支援的經驗,因為當時有時程上的壓力;所以我在加入團隊後除了重點了解自己要負責的 Feature、團隊的 Coding Style 外,會主動向定下這些需求的 PM 討論,以此了解因果關係並快速融入團隊。

也曾接手過要維護及擴充的專案,因為當時沒有相關文件可以參考,為了快速掌握專案,我一邊操作系統一邊畫心智圖幫助自己釐清功能;了解系統架構後再去找合作窗口確認需求,避免實作方向與客戶需求不同。

在修改 Legacy Code 時,我會特別注意前人是否有撰寫測試案例,在程式能有效測試的狀態下才能安心的修改與擴充。

以正在運行的專案來說,我會希望建立團隊 Code Review 機制,讓每個人熟悉彼此的程式、使用共同的 Coding Style,當知識共享與風格統一後,就能減少 Legacy Code 的產生。


3.2 有用什麼工具讓團隊有一致的 Coding Style

考點:是否有善用工具解決問題

我在 VSCode 有安裝一個 Prettier 的外掛,靠它就能非常有效的將程式碼格式化;不過有時為了保證團隊的 Coding Style 會導入 ESLint 來統一風格,但 Prettier 與 ESLint 一起使用時會遇到衝突,所以我有再安裝一個 Prettier ESLint 的外掛來解決這個問題;讓程式不僅符合團隊的 Coding Style,還可以透過快速鍵將程式碼格式化。


寫完這篇文章時,我的心裡突然冒出一段話:「一開始我們在追求問題的解答;到後來,我們在學習解決問題的方法。」

感謝大家的閱讀,如果喜歡我的文章可以訂閱接收通知;如果有幫助到你,按Like可以讓我更有寫文的動力,我們明天見~

參考資源

  1. 【翻譯】JavaScript 設計模式
  2. 什麼是 Design Pattern?
  3. 我為什麼想學設計模式 ( Design Pattern )
  4. 設計模式五大基本原則 SOLID


參考資源(圖 1)

在許多人的幫助下,本系列文章已成功出版,除了添加新的篇章,更完善了每個案例的應對進退;如果對現在的職涯感到迷茫,也許這本書能帶給你不一樣的觀點~

天瓏書局: https://www.tenlong.com.tw/products/9786263334571

Talk

留言討論

留言載入中…