前言
文章內容源於《程式碼大全》一書,感興趣的同學可以直接閱讀書籍。
程式碼大全一書中從序到正文開始,無不提到一詞構建,那麼軟體構建到底是什麼?其實在序中已經解釋:從軟體生命週期開始,到編碼完成。
⭐程式碼構建 一般佔據了小型專案的 65%、中型專案的 50%。
⭐因此構建要為小型專案的75% 中大型專案的 50%-75% 的錯誤負責
當然,實踐也表明: 修正上述由於構建造成的錯誤付出的代價,往往比修正 由於需求和架構變更帶來的錯誤代價要小很多。
但是,這也造成了開發人員的誤導:
如果不修正這些構建造成的錯誤,往往代價是最直接的。比如一個系統錯誤或者資料維護異常,往往比 需求變更 對 軟體產品的 好壞定義來的更直接。 說白話一點,如果出現系統錯誤、或者資料維護異常,那麼軟體產品肯定不是一個好產品。但是如果 軟體產品是 可用的,只是缺乏帶給使用者更好的體驗,那麼至少是一個可用產品。
往往需求變更要付出的代價,其實在構建階段,如果採用了更好的實踐方式,那麼面對需求變更,是有更好的響應變化的能力。因此,做好構建是至關重要的。
軟體構建?
從構建的產物看構建?
構建的產物——原始碼——也是對軟體的精確描述(勝過需求規格書和設計文件,程式碼總是最新的,不會過期)
什麼是構建?構建的核心是什麼?
軟體開發的核心就是軟體構建,而軟體編碼則是構建的核心。軟體構建主要活動有:詳細設計、編碼、除錯(debug)、自測(單元測試和整合測試)、整合(聯調對接)。
隱喻?
中文含義: 比喻的一種、不用如、像、似、好像等比喻詞彙,而是用是、為、成為、變為等詞彙來描述。把某一事物比作和他有相似關係的另一事物。例如:少年是祖國的花朵、荷葉成了一把撐開的傘。
最熟悉的隱喻應用場景?
透過把不太理解的東西和一些你較為理解的東西比較,可以對不太理解的東西產生較好的理解。這種方式就使用了隱喻、也稱之為建模。
軟體隱喻的理解程度,代表了對軟體開發的理解深度
軟體工程學中沒有標準的隱喻手段,每個人隱喻的手段不同,對軟體的理解程度則不同。大到軟體需求,小到一個方法,一行程式碼,不同的人見解是不一樣的。即軟體隱喻的理解程度、代表了對軟體開發的理解程度。這點不可否認,往往更具有經驗的開發人員,對隱喻的理解更深。
軟體隱喻為了幹什麼?
軟體隱喻並不是告訴你去哪裡尋找答案,而是告訴你該如何尋找答案。舉個例子:當你遇到一個報錯,隱喻並不是告訴你是否該去百度或者書籍中尋找答案,而是告訴你如何從報錯中,尋找解決這個報錯的答案。
⭐軟體隱喻 不是答案、不是演算法、而是解決思路
軟體隱喻場景有哪些?
舉個例子:前段時間身份中心開發一個小需求,生成郵箱名,遇到問題是,多音字複姓的處理。由於Git開發拼音包的處理和各類開源框架的漢字拼音處理包多音字處理都有限 ,為了最大靈活度。採用了專案YML配置,存在一個小問題是:怎麼透過yml配置的K對應的Value來替換姓。有兩種解決方案:
- 使用Java自帶的字元處理類,進行切分替換,但是程式碼判斷很多,可讀性很差。
- 使用隱喻,這類問題可以是尋找一個字串的最大子串問題,使用演算法來解決,提升效率。且將演算法定義為最小方法,使用封裝的特性,方便引用。
演算法和軟體隱喻的區別?
演算法直接給你解決問題的指導,而隱喻告訴你該如何發現這些指導資訊,去哪裡尋找。
⭐ 演算法像是已存在的解決方案,如果可以直接使用,那麼程式設計是最方便的。但是程式設計的難點在於問題概念化。而且程式設計的大多數錯誤都是沒有更準確的將問題概念化造成的問題。也就是說:演算法有很多,但是為什麼不能直接使用? 對映到實際工作中: 演算法有很多、排序啊、查詢啊、最短路徑、最大子串、迴文等等。但是實際工作中,需求並不會直接告訴你用什麼演算法去完成什麼?而是什麼時候用什麼演算法?將需求的核心問題概念化,在透過隱喻來使用已有的解決方案。
⭐演算法的產生本身也是一種隱喻的手段:現實社會中已經存在的問題,或者理論,想要解決這些問題或者提出更好的理論,本身就是將某種實際問題的解決方案 隱喻成為了通用演算法。
常用的軟體隱喻場景?
-
寫作 —— 編寫程式碼
將編寫程式碼 比喻為 寫作,但是實際上寫作常常要丟掉草稿。在軟體編碼中,這是代價很大的操作,最常見的就是寫到一半,發現思路不通,或者有更好的解決方案,刪掉重寫。 所以在編寫程式碼時,關鍵點是 當第一次嘗試時就讓程式碼走在正確的方向上。或者在成本最低的時候做修改。
-
培植耕作 ——編寫程式碼
將編寫程式碼比喻為耕作,意思是一部分一部分的開發施肥,一點點的將成果新增過軟體產品中。這樣的話開發的方式和過程不能控制。例如開發第一部分和開發第二部分,採用的開發方式和過程(比如分析、設計、實現)第一部分和第二部分之間存在關係,那麼就是不可控的,因為只有兩部分全部完成後,才能一起整合。
⭐類似瀑布模型。
-
養殖 —— 編寫程式碼
將編寫程式碼比喻為養殖,先搭建好框架和架構、涉及實現的細節可以建造好虛擬的類,在架構形成之後,再把真實的類一點點放進去。
⭐支援上述方法論的有演進式交付,也是敏捷開發的基礎,敏捷開發就是迭代,循序漸進的開發方法。
-
建造 —— 編寫程式碼
將建造房屋比喻為編寫程式碼,先要做總體設計即軟體架構設計,再出藍圖,即軟體詳細設計,最後要做建造審檢,即軟體複查(程式碼走讀)和審查(程式碼稽核)。
⭐其實很多軟體學中的術語都是從建築學中演變而來的:軟體架構(建築學)、支撐性測試(腳手架)、構建(建設),這一點體現在不少開源框架的英文官網詞語就是建築學的常用語。
⭐建築學中結構一旦出現問題,代價是毀滅性的。因此要進行規劃和建設。同理,大型商業軟體專案,往往需要做更高規的設計,來保證專案的平穩和發展。
-
工具箱 —— 編寫程式碼
將編寫程式碼比作工具箱,即編寫程式碼,在合適的時候採用合適工具,軟體領域中,往往不要依賴於某一種 手段或者方法,更多時候,有更多比較好的手段去解決問題。 -
各種隱喻的組合
隱喻是一種啟發式手段,而不是演算法,但是隱喻沒有標準的規範,因此,適當的引申,結合經驗去隱喻。
總結:
前期準備
前期準備的重要性
準備工作的是目標什麼?
準備工作的目標是為了降低專案風險,讓專案平穩計劃進行。而專案前期的主要風險在於:
- 風險1:糟糕的需求分析
- 風險2:糟糕的專案計劃
準備工作不足的原因是什麼?
準備工作不足的原因主要有:
-
開發人員並不具備前期的準備技能,例:專案規劃、創作案例、分析全面準備的需求
-
過早的進行編碼工作,一部分原因在於沒有經驗的開發者 有儘快開始編碼的慾望、一部分原因在於管理者對於 花時間進行前期準備的程式設計師的不支援和不理解。
⭐作者趣稱這種現象 為WISCA綜合症狀或者WIMP綜合症狀。Why Isn't Sam Coding Anything? (為什麼Sam不在寫程式碼?)Why isn't Mary Programing?(為什麼Mary沒有在程式設計?)
如何解決準備工作不足?
-
學習更多的技能,提升自己的水平,學會如何做好前期準備工作,如何做好需求分析
-
與管理者溝通,告訴他前期準備工作得重要性,優秀的管理者懂得聆聽別人的意見
- 訴諸邏輯:要知道搞清楚需要做一個什麼樣的產品,往往比先做一個錯誤的東西,在扔掉重頭做的成本低得多這樣的道理
- 訴諸類比:程式設計師是軟體食物鏈的最後一環,產品經理吃掉需求, 架構師吃掉架構設計,而程式設計師笑話設計。這是一條健康的軟體食物鏈。
- 訴諸資料:IBM、惠普、TRW等公司的研究表明:在構建一開始清除一個錯誤,返工的成本是在中期甚至後期的十分之一百分之一。
- 提前預留出自己做好前期準備工作的時間,看起來評估工作時間的效率不高,但實際上幹活的質量卻有了保證。
辨明你所從事的軟體型別
商業專案的計劃、需求、架構活動與構建、系統測試、質量保證交織在一起,性命攸關的專案往往採用了更序列的方式,同時需求穩定是保證可靠穩定性的必備條件之一
- 前期準備工作對 迭代開發和序列式開發的影響
⭐忽略前期準備工作的 迭代式開發,比關注前期工作的 序列式開發成本更高。
⭐成功構建的關鍵之一:理解前期準備工作的完成程度,根據此來調整開發方法。即:專案初期/需求初期,預估採用迭代式開發,5人/日工作量,當前期工作準備快完成時,對需求和專案的理解已經更加深入,這時候不能太死板,要變化著調整開發方式,和工作量的安排。
- 迭代式開發和序列式開發
什麼時候選擇迭代式?
-
- 需求相當穩定
- 設計理解透徹
- 開發對領域很熟悉
- 專案風險小
- 專案長期的可預測性
- 後期改需求,返工成本昂貴
什麼時候選擇序列式?
-
- 需求理解不透徹
- 設計複雜有挑戰
- 開發對領域不熟悉
- 專案有諸多風險不可控
- 後期改需求,成本較低
問題定義
為什麼需要問題定義?
在構建開始前,有一項先決條件:對這個系統要解決的問題做出清楚的陳述。
如何判斷已經寫好了問題定義?是否能成為構建活動 的良好基礎
- 問題定義在需求分析之前,而需求分析是對問題的深入刨析
-
問題定義應該站在客戶的角度,用客戶的語言來描述
⭐如果沒有好的問題定義,那麼可能解決的是一個錯誤的問題。
需求
需求是什麼?
需求:詳細描述軟體系統該做什麼。需求活動有:需求開發,需求分析,需求定義,軟體需求,需求功能規格書。
為什麼需要需求?
明確需求,可以很大程度上減少成本。參照3-1表,需求階段沒有發現的缺陷,越往後,修復花費的成本代價越高。因此需求越穩定,那麼成本浮動就會越少。
需求的穩定性?
穩定的需求是開發的聖盃,然而實際上,專案越長,客戶對專案的理解程度會隨著參與度而增加。提出的需求隨著專案週期和一開始可能大相徑庭。IBM的研究也表明:平均每個專案會有25%的需求變更
在構建中處理需求變更?
- 檢測需求的質量:透過核對表核對需求的質量。如果需求質量過差,從新開始進行需求分析,直至穩定。
- 確保所有人都知道需求變更的代價:不僅僅是開發人員需要了解,從客戶到專案經理,產品經理,架構師,測試都需要知道需求變更的代價。明白了需求變更的代價,才能更好的做好穩步的軟體構建。
- 建立可控制的需求變更規範:如果可以和客戶制定一套可控制的需求變更流程。那麼在規範內,需求變更就是週期性的
- 使用適應變更的開發方法:例如演進交付,分階段交付,一次交付一部分,透過反饋結果調整設計,再交付下一部分。主要手段是縮短開發週期,在短時間內,將開發成果做展示來控制需求變更。
- 放棄:在需求極度糟糕的狀態下,需要放棄,不做不可回收成本的消耗。
- 從商業價值來評估需求:部分需求可能會很好,但站在商業價值上來看,可有可無。那麼就需要考慮需求是否值得考慮。
需求核對表
架構
軟體架構是什麼?
架構是軟體設計的高層部分,用來支撐軟體細節實現。架構也成為:系統架構,有一份獨立的架構規格書文件描述。
架構對構建的影響?
修復架構中的缺陷所花費的成本如3-1所示,因此越穩定越詳細的架構,構建開展的越快。相反,糟糕的架構設計,構建活動會無從下手。
架構的組成部分
- 程式組織(包結構定義)
- 主要類的詳細定義(程式入口,核心類定義)
- 資料設計(資料庫的設計,資料的互動方式)
- 業務規則(程式的業務規則定義,例如這部分客戶需要響應30S完成等)
- 使用者介面設計
- 資源管理(資料庫連結,執行緒管理、記憶體設定)
- 安全性(考慮程式的安全性,提前建立威脅模型,對資料和程式進行保護)
- 效能(在記憶體、成本上需要考慮效能目標)
- 可伸縮性(描述系統增長之後,架構如何應對使用者數量、伺服器數量、網路節點數量、資料量的變化,系統的伸縮性)
- 互用性(如果系統和其他軟體或者硬體共享資料,那麼架構需要描述清楚)
- 國際化/本地化(國際化:I18n,讓程式支援多個地域的技術。本地化:L10n,翻譯程式支援當地特定語言。架構需要考慮國際化之後,字符集和字串型別,如何不更改程式碼就能正常使用,架構應該能夠描述清楚。)
- 輸入輸出(架構應該描述定義讀取策略,描述IO錯誤記錄在那一層:欄位、記錄、流、檔案)
- 錯誤處理(架構應該詳細定義如何處理異常和錯誤)
- 容錯性(系統的容錯性怎麼做的?在遇到錯誤時,是直接不可用,還是提示。或是部分可用?何時恢復?微服務中Hystrix元件就是為了解決服務不可用,整個系統的容錯性)
- 架構的可行性(考慮架構的實現需要的硬體和軟體條件,再架構工作開展之前需要解決這些條件)
- 健壯性
- 買/賣決策(即宣告架構中的元件是買/還是自造)
- 複用性(架構應該宣告是否複用業界中已經存在的軟體)
- 變更策略(描述架構對於功能增強 或者 變更的支援)
- 總質量(架構應該明確風險區域,解釋哪些地方存在風險,並給出解決方案和規避步驟)
架構質量核對表
花費在前期準備上的時間長度
前期準備需要做哪些?花費時間週期?
前期準備工作包括問題定義、需求分析、架構設計。一般來說,佔總工作量的20-30%時間比較合理
前期準備工作核對表
構建決策
當構建的前期準備工作已經完善,接下來需要做的就是明確構建的實現過程,即程式語言,程式設計約定,構建方法。
- 選擇程式語言
這部分可以參考專案型別表,做好了前期準備工作,明確了專案型別。採用何種程式語言,可以借鑑已經有名聲的經典案例。決定自己構建的語言。
- 程式設計約定
變數命名、包結構、類定義、子程式名稱和定義、註釋規範、格式規約。通常能提供一個標準的程式設計手冊最好。
- 你在技術浪潮中的位置
確定構建語言的穩定性,如果在語言最火的前期使用,那麼就需要接受語言的一定缺陷和不穩定。一門好的語言一定是受過時間的磨鍊才鍛造出來的。
- 選擇合適的構建實踐方法
透過對比構建實踐表,來確定是否有合適的構建實踐
構建實踐核對表