每一個可以努力的日子,都是一份厚禮。
約定優於配置——軟件開發的簡約原則
工作快滿 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 曾經談過關於 技術佈道 的一些經驗,其實就是“用影響影響影響”。那些大牛們孜孜不倦地對外傳遞大公司的動態、新的技術趨勢以及他們自己對技術的思考感悟,用自己的影響力去影響一部分具備影響力的人,再促使這部分人自發的去影響更大的目標群體。一些人之所以更牛是因為他們專註於自己做的事情,認為它是重要的,滿懷熱情,所以他有動力去優化自己的工作。相比之下更多的人只不過是在應付。
我沒有那些令人景仰的牛人的魅力,我所能做的,只是把自己所知道的總結出來,教給和自己合作的開發者,讓他們也能在技術上有所提升,這樣合作起來也會更輕鬆。我想這是件令人愉快的事情。
這篇文章由lovelucy於2012-09-15 13:32發表在編程。你可以訂閱RSS 2.0 也可以發表評論或引用到你的網站。除特殊說明外文章均為本人原創,並遵從署名-非商業性使用-相同方式共享創作協議,轉載或使用請註明作者和來源,尊重知識分享。 |
批評不自由
則讚美無意義
Mozilla Firefox 47.0 Windows 7 大約8年前
我覺得每個函數都簡到十行以內都是不可能實現的。如果是這樣的話,是要幾十個函數層層調用才能做完一個功能嗎
Google Chrome 21.0.1180.71 Windows 7 大約11年前
重構,唉
Google Chrome 22.0.1229.94 Mac OS X Lion 10_7_3 大約11年前
💡 你好,兄弟,你的總結非常好,受教了。
不過在你寫的“重構”部分,我並沒有看到你對重構的認識,可否分享霞你是如何重構的?
最後,感謝你,嘿嘿,你給我們解釋東西的時候,可以多加一些實例。辛苦
Google Chrome 22.0.1229.94 Windows 7 大約11年前
其實就是“使用繼承、多態等面向對象手段,還有廣泛採用的工廠模式等等”
可以看看我另外一篇關於MVC的,《患上肥胖症的controller》
先學習使用一個框架,會發現框架設計的精妙,然後就去看框架的源碼,收穫很多。
我也只是剛入門而已,呵呵 🙂
Mozilla Firefox 23.0 Ubuntu Linux 大約11年前
重構的那一段採用反射機制會不會更好?PS:我沒怎麼寫過js,不知道js的反射機制怎麼樣。