工作快滿 1 年了。在剛從 CUHK 畢業的時候,我還滿懷悲壯地表示在中文大學飽受摧殘,各種 assignments projects 所寫的代碼已經超過了整個在武大的四年。而現在,wc -l 顯示的數字已經可以讓我十分蛋定,這一年的代碼量已經超過了過去所有學生時代的總和。回過頭來看一年前寫的代碼,真是慘不忍睹,恨不得把當時的自己拉出去暴打一頓。確實走了很多彎路,但我也因此收穫頗豐。

感觸之一就是怎樣編寫漂亮整潔的代碼。我曾經說過,我是個完美主義者,寫博客的時候我要檢查以防出現錯別字,寫代碼的時候我會特別注意變量命名是否規範,甚至代碼縮進是否對齊。這大概已經屬於強迫症的治療範圍了。在現實項目開發中,整潔漂亮 意味着用最少的代碼,實現完整的業務功能,同時代碼是易於理解的。然而隨着時間流逝代碼不斷被修改,系統設計的整體結構則逐漸衰弱。編碼從嚴謹的工程墮落為隨性地胡亂砍劈。不重構,軟件就會慢慢腐爛。這一切是如何發生的呢?

歸根到底,還是源於軟件開發人員惰性。懶惰是天生的,例如 sofish 舉例過的一段代碼——

if(condition_a) {
    var str = condition_a.trim().replace(/^{(\w+)}$/, function(match){
        // ...
    });
} elseif(condition_b) {
    var str = condition_b.trim().replace(/^{(\w+)}$/, function(match){
        // ...
    });
} elseif(condition_c) {
    var str = condition_a.trim().replace(/^{(\w+)}$/, function(match){
        // ...
    });
} else {
    var condition = defaultValue;
        str = condition.trim().replace(/^{(\w+)}$/, function(match){
        // ...
    });
}

如果又有新的 condition_d,懶人複製粘貼一下就搞定。你可以同樣改成 switch,但本質上都是在寫重複的代碼。DRY 原則,即 Don’t Repeat Yourself,是編寫高質量代碼毫無疑問最重要的原則,重複是所有邪惡的來源。上面的代碼可以稍作修改

function dry(condition) {
    condition = condition || defaultValue;
    condition.trim();
    return condition.replace(/^{(\w+)}$/, function(match){
        // …
    });
}
 
if(condition_a) {
    dry(condition_a);
} elseif(condition_b) {
    dry(condition_b);
} elseif(condition_c) {
    dry(condition_c);
} else {
    dry();
}

將重複的邏輯提取出來,抽象為一個獨立的函數,看上去就乾淨漂亮很多。所以抽象是一個很重要的能力。說一個差勁的程序員懶惰,並不是指他不情願編碼,事實上,他正不遺餘力的敲打鍵盤;懶惰體現在不願意思考,不想碰架構優化現有的系統。

為什麼需要架構?

架構是當事情變得複雜之後必須考慮的問題。如果你的桌上有 2 本書,那麼你怎麼擺放其實沒關係,你隨手翻翻也能很快找到你想要的段落。如果你有幾百本書,那麼你就需要一個書架了。如果是一個圖書館,那麼就更需要一個合理的規劃,將書本分門別類,編號,建立索引,方便查找。軟件開發是一樣的道理,你得知道系統將要到一個什麼樣的規模,提前做好各方面的約定。例如選用一套開發框架,指定團隊的代碼規範。

約定優於配置

這是 maven 最核心的設計理念,很多語言框架都將其思想發揚光大。遵循約定雖然損失了一定的靈活性,不能隨意安排目錄結構,不能隨意進行函數命名,但是卻能減少配置。更重要的是,遵循約定可以幫助開發人員遵守構建標準。

例如 Yii 的典型項目結構是:

projectName/
   index.php                 Web 應用入口腳本文件
   assets/                   包含公開的資源文件
   css/                      包含 CSS 文件
   images/                   包含圖片文件
   themes/                   包含不同風格的主題
   protected/                受保護的應用文件,主要代碼在這裡面
      components/            包含可重用的用戶組件,自定義類等等
         Controller.php      所有控制器類的基礎類
         Identity.php        用來登錄認證的 'Identity' 類
      config/                包含配置文件
         main.php            Web 應用配置
      controllers/           包含控制器的類文件,MVC 中的 C
         SiteController.php  默認控制器的類文件
      data/                  包含示例數據庫
         schema.mysql.sql    示例 MySQL 數據庫
      extensions/            包含第三方擴展
      messages/              包含翻譯過的文本,適用於多國語言支持
      models/                包含模型的類文件,MVC 中的 M
         LoginForm.php       'login' 動作的表單模型
      tests/                 包含測試腳本
      views/                 包含控制器的視圖和布局文件,MVC 中的 V
         layouts/            包含布局視圖文件
            main.php         所有視圖的默認布局
            column1.php      使用單列頁面使用的布局
            column2.php      使用雙列的頁面使用的布局
         site/               包含 'site' 控制器的視圖文件
            index.php        'index' 動作的視圖
            login.php        'login' 動作的視圖

又例如 Yii 中對於 action 通配符的規範,XyzController.php 中若有一個名為 actionEdit() 的方法,那麼體現在網頁 URL 上,就是是除去 action 前綴的動作函數名。即 http://www.mydomain.com/xyz/edit

更多的規範還包括變量、函數和類使用駝峰命名風格,即每個單詞的首字母大寫並連在一起,中間無空格。變量名和函數名第一個單詞全部小寫,而類的首字母需要大寫,例如:$basePath, runFunction(), LinkPager。對 private 變量和函數來說,以下劃線作為前綴,例如 $_actionList

當然還有數據庫的規範——

  • 數據庫表名和列名都使用小寫命名。
  • 名字中的單詞應使用下劃線分割 (例如 product_order)。
  • 對於表名,你既可以使用單數也可以使用複數。但不要同時使用兩者。為簡單起見,推薦使用單數名字。

如果風格統一,那麼代碼將是有規可循的。我們可以根據命名了解結構,根據結構理解構建的代碼原理。團隊的合作將是無縫的,多人 debug 也將沒有障礙。

我之前做的一個項目,就是因為缺乏遵循規範,而花費了大量時間和精力去做適配不一致的接口參數,各種變量名稱的轉換佔了整個項目代碼的很大部分。親自動手寫好一個站點並自己進行代碼維護,理解其中的弊端,經歷無數次的捶胸頓足痛定思痛,才知道最終如何改進才算架構“合理”。

關於重構

進度預估實際上是比較困難的一件事情。每當 Leader 問我這個模塊要幾天搞定時,我都支支吾吾。因為有時需要對舊代碼進行改造,或者說重構,而這些工作對於功能上沒有任何產出效益。在版本發布的重壓下,快速迭代只是我個人的一個期望而已。

軟件開發已經進化為軟件設計。在設計當中應當注重簡約的原則,少就是多。按照一定的設計模式來構建,總能達到事倍功半的效果。例如使用繼承、多態等面向對象手段,還有廣泛採用的工廠模式等等。又例如每個函數都簡單到僅僅 3 到 4 行長,函數的縮進層級不多於三層,每個函數只做一件事,可復用,同時充分解耦。

好的代碼需要打磨。一個優秀的程序員,總能保證每一次 commit 倉庫里的代碼比上一次更好。

關於技術佈道

Fenng 曾經談過關於 技術佈道 的一些經驗,其實就是“用影響影響影響”。那些大牛們孜孜不倦地對外傳遞大公司的動態、新的技術趨勢以及他們自己對技術的思考感悟,用自己的影響力去影響一部分具備影響力的人,再促使這部分人自發的去影響更大的目標群體。一些人之所以更牛是因為他們專註於自己做的事情,認為它是重要的,滿懷熱情,所以他有動力去優化自己的工作。相比之下更多的人只不過是在應付。

我沒有那些令人景仰的牛人的魅力,我所能做的,只是把自己所知道的總結出來,教給和自己合作的開發者,讓他們也能在技術上有所提升,這樣合作起來也會更輕鬆。我想這是件令人愉快的事情。

參考鏈接:
MVC架構導致的詞彙表分裂
每一件意義重大的小事——讀《代碼整潔之道》