結合自己經歷聊聊注重實效的程式設計師應該掌握的幾個原則

渡碼發表於2019-08-13

本篇文章是《程式設計師修煉之道》第二章的筆記,總結了高效程式設計師需要遵守的一些原則和常用的開發模式,對我們有非常重要的指導意義。建議每個程式設計師都應該學習並掌握這些原則。如果大家覺得這個系列文章有價值,我們可以組織一次抽書的活動,鼓勵大家從原文學習。

DRY 原則

軟體開發過程無時無刻都伴隨著維護,如果專案中有大量的重複程式碼會對我們的維護工作造成很大的麻煩。比如:一段程式碼在多個地方出現, 一旦要修改就需要我們同時修改多個地方,如果某個地方忘記修改就可能導致一些不必要的錯誤。因此作者提出 DRY原則 - Don't repeat yourself(不要重複你自己)

我們平時有不少重複的場景, 同時也有避免重複的解決方法,下面舉幾個例子。

  • 程式碼與註釋:我們經常被說教要在程式碼里加註釋。但註釋並不是越多越好,我們應該把註釋留給高階的說明,對於比較低階的說明用程式碼代替註釋即可。否則,就存在重複的知識,每次修改程式碼都要想著修改註釋
  • 程式碼與文件:文件的更新是永遠滯後於程式碼的,經常更新了程式碼但忘記更新文件。我們可以採用一些輔助技術或者自研的技術,比如:API doc、Java doc 之類的工具,程式碼更新後,文件會隨著更新
  • 設計中的重複:假設某個類中有個起始座標和結束座標兩個屬性,這時再加一個兩點間距離的屬性就有點重複了,因為我們可以根據起止點計算出距離
  • 臨時性的重複:有時候我們臨時做一些需求可能用到了專案中某段程式碼,我們一般會直接複製過來用,這時候也存在重複。更徹底的做法是我們可以把一些比較常用的計算邏輯抽象成介面,形成自己的工具包,需要的時候直接引用即可,無需拷貝程式碼。比如:一些常見的求和,求最值等需要 for 迴圈的處理邏輯是不是可以抽象一個 reduce 函式
  • 開發者之間的重複:這個是比較常見的重複,如果一個專案的兩個組同時開發很容易造成同一個小的邏輯在兩個組的成員有各自的實現,這種重複不太好避免。但我們可以增加團隊之間的交流,可以組織一個平臺,把一些比較好的元件開放出來。我做資料開發時經常會用到一些 udf 處理資料,如果我當前專案中沒有,我會到公司的公共平臺去搜相應的關鍵詞, 一般都能找到現成的 udf。我比較建議團隊內部開源不同的專案程式碼,當然是不涉及機密程式碼的前提。我在開發中經常會問上游同學資料怎麼處理的,經過了什麼邏輯,這樣做一方面增加溝通成本,二來畢竟耳聽為虛。所以我就經常反編譯上游的程式碼,以後需要看邏輯不需要問別人,直接看程式碼節省非常多的時間。當然開源也有個好處是我們可以學習別人程式碼中優秀的地方。

正交原則

正交性就是不相互依賴性或者解耦性。如果兩個或多個事物中的一個發生變化,不會影響其他事物,那麼這些事物就是正交的。對應到程式設計,就是我們常說的高內聚、低耦合。正交的系統好處非常多,正交的系統可以降低風險,當系統中某個元件修改時,只要對外的介面保持不變,該元件就可以任意修改而整個系統不會受到影響。正交的系統能提高生產率,因為系統元件是解耦的,因此各個元件可以獨立地、並行地進行開發。比如最近幾年比較火的前後端分離技術就是很好的代表,以前後端的同學經常需要寫一些套頁面的前端程式碼,前後端同學的程式碼交織在一起相互依賴。但前後端分離後,只需要把協議確定好,前後端技術就可以解耦,開發同學就可以並行開發,提高生產率。下面列舉幾種維持正交性的方法

  • 設計:設計系統時我們可以採用基於元件的分層架構。每層提供一級抽象,每層只是用其下面的層次提供的抽象,層與層之間的協議/介面穩定且可擴充套件,下層元件的改動對上層透明
  • 編碼:為了讓程式碼保持解構,我們可以開發 “羞怯” 的程式碼,對於不需要開放出來的邏輯,可以控制其訪問許可權,不被其他元件引用。避免使用全域性資料,全域性資料涉及多個使用方同時更新,導致元件耦合度較高。避免編寫相似的函式,相似的函式往往有重複的程式碼,使得修改程式碼要同時更新多處
  • 測試:在進行單元測試時,如果某個單元牽扯系統其餘很大一部分,說明該單元與系統其他部分耦合度較大,需要引起開發同學的重視

可撤銷原則

可撤銷原則是說,當系統中某個部分需要改變時,我們能不能很好的撤銷已有的程式碼,靈活的適應新變化。因為需求無時無刻都在變化,因此可撤銷性就一直存在。舉個栗子:假設我們開發一個資料庫視覺化軟體,最開始的需求是基於 Mysql 的,如果我們不做分層設計,凡是需要增刪改查的地方我們直接寫 JDBC 程式碼。但是某一天我們底層資料要支援 MongoDB 怎麼辦,因為我們的 JDBC 程式碼分佈在專案的各個模組的程式碼中,幾乎無法撤銷,這時候系統只能重寫。如果我們設計之初將資料訪問層作為一個元件抽象出來,那麼上層只需要呼叫抽象出來的介面來運算元據庫。這樣做的好處是當需要支援其他資料庫時,只需要為該資料庫編寫支援我們抽象介面的資料庫訪問程式碼即可,因此係統就具備可撤銷性的。其實,我們在 Web 專案中經常使用 ORM 框架也是基於可撤銷原則的,ORM 可以將資料庫表對映成物件,底層的增刪改查對上層透明,所以可以靈活地調整底層資料庫。

曳光彈與原型

在黑暗中需要打擊軍事目標時通常會使用曳光彈,它在槍與擊中的地方之間留下一條煙火般的蹤跡,用來指示彈道和目標,從而協助射手修正彈道。其實,黑暗中的目標就像我們開發中面對的未知系統。面對未知系統,如果我們製作大量文件,逐一列出每項需求,嘗試確定所有未知因素,就猶如在黑夜中對目標未知預先進行大量計算然後射擊,很顯然這種情況需要消耗大量計算力並且不一定能擊中目標。而注重實效的程式設計師往往更喜歡使用曳光彈。曳光彈的核心優勢就是反饋是及時的。比如:我們要開發一個支援多種序列化格式的 RPC 框架,最開始我們是不是可以先用簡單的系統預設的序列化方式先將整個系統框架搭建起來,先讓系統能夠執行起來。執行後我們可以及時地得到使用方的反饋,修復已有問題、增加多種序列化框架、不斷迭代完善。

介紹完了曳光彈再來說說原型,記得在大學學習《軟體工程》時就接觸了原型開發。原型更像是一個 Demo,不注重程式碼的實現,而是能夠快速出一個能夠與產品確定需求的東西。比如:我們需要確定某個系統的 UI 需求,我們可以用最快的開發語言,開發出一個 Demo,它的程式碼不需要規範,互動介面也不需要太美觀,因為原型大概率不會在後續實際的專案實現中使用。

接下來簡單總結一下這兩種開發模式的區別。曳光彈強調的是明確需求後,我們能不能開發出一個麻雀雖小、但五臟俱全的系統來快速獲得反饋,從而指導我們進一步迭代。在曳光彈開發模式下,後續程式碼是依賴於第一版的程式碼。而原型開發更側重明確需求這個階段,快速開發一個 Demo ,目的是為了能夠基於原型確定系統的需求。這個階段不在乎用什麼程式碼、不在乎系統的完整性、健壯性以及正確性,因為原型程式碼基本不會用在真實的系統開發中。

領域語言與估算

這節內容我覺得平時應用不多,理解的不深刻,因此就簡單總結。領域語言我的理解就是使用(創造)一門規範的虛擬碼,為什麼是虛擬碼呢?假設我們與產品溝通需求,我們可以使用文字,但我們都知道中華文字博大精深,別人表達的意思跟我們的理解可能不一致,否則的話我們也不需要這麼苦逼的加班,當然直接用程式語言溝通更不可行。那麼就需要一箇中間層的虛擬碼,既能清晰的表達出需求,又能讓產品或者使用不同程式語言的程式設計師都能看懂。

對於估算這一節,作者介紹了一些常見的預估問題(估算專案時間、流量)的一些指導性意見,但感覺比較偏理論,實操性不強。但給我印象最深的一句話是:在咖啡機旁給出的估算將像咖啡一樣回來糾纏你。也就是說估算不等於不假思索的回答,我們可以用 “這個問題我現在答覆不了,我回去想下” 之類的話術先應付一下,後續再詳細地思考。

小結

本章主要介紹了三個原則中,這三個原則如果在開發中加以總結和利用將對我們高效地開發非常有幫助。DRY 原則避免重複勞動,正交原則避免改動一個元件時牽扯整個系統的維護,可撤銷原則避免更換系統某個元件時導致系統崩潰。最後,介紹了曳光彈和原型開發兩種快速開發模式,尤其是曳光彈,我比較喜歡用,先小規模、小成本搭建骨架並跑起來,後續再不斷地豐富骨肉,希望以上介紹的內容對你日後開發有幫助。

 

歡迎關注公眾號「渡碼」,我將分享更多優秀書籍的內容,組織定期抽書活動

 

 

相關文章