軟工Chatper four
有一種說法,未來的程序都是機器生成的,程序員將成為一種古老的職業種族。這種事在人類歷史上並不罕見。歷史上有很多一開始都是靠人做的職業,最後都由於技術的更新換代而成為了一種被人遺忘的藝術。
就算是在編程領域,如今大部分人也不必寫匯編代碼。但是由於抽象泄漏法則,實際上大部分程序員(軟件工程師)都還是需要從頭到尾都關心。最近一段時間熱炒的概念是“全棧程序員”。其實,一個程序員如果要解決問題,解決問題所要面對的技術都會被按需學習。每個職業的人都喜歡為自己的職業貼金,有很多程序員打心底裏都認為編程是一種藝術而非技術,雖然他們日常開發更多依賴的其實是技術。
說了這麽多,軟件裏的程序部分,目前還是需要人來做;軟件的裏的工程部分,也是需要人來做;這兩者目前的情況屬於半自動化。
大部分情況下,程序員首先要在個人技術和流程上有一個密集的階段積累。無論是對編程語言的學習;還是在工程上依賴經驗進行入門;但更重要的應該是反復的練習和實踐。
我們再看下:
軟件=程序+軟件工程
單看程序,按下工程不表。下面的公式對麽?
程序=算法+數據結構
這要看抽象的角度。俠義的說,算法和數據結構是非常Core的部分,一般只是占一個程序的Base部分。而一個Base部分,充其量只是一個程序的一個模塊,即使是很核心的模塊。
假設一個程序只用一種語言開發,一個語言包括運行時+基礎類庫。一個稍微有點規模的程序需要在語言運行時+語言基礎類庫的基礎上,引入3rd(第三方)庫,引入所需要的框架或者自己造,開發各種輔助支持庫,並構建所要解決問題的領域模型,然後組織上層業務邏輯。
這些,中等規模粒度下,基本單元可以看成是一個模塊。所以一個程序可以看作是
程序=語言運行時+基礎類庫+3rd庫+框架+領域模型+業務模塊。
而一個模塊,在大部分語言下,由函數和類構成。在一個類的角度來看,可以考察的是類間結構和類內部結構。類間結構表現出來可能是設計模式,類內結構,可能才是數據結構+算法的領地,但也並非所有類都是數據結構,但他們都是一個有限狀態機。
說了這麽多,拆分到類,類下的基本行為單元還是函數。
所以,如果我們保證所有函數的行為正確,就能很大程度保證一個類的行為正確,從而保證一個模塊的行為正確,而所有模塊的正確最後就保證了一個程序的正確性。
怎樣保證函數的正確性?我們知道語言有強類型和弱類型,強類型要做的事情就是通過類型的安全來保證程序的靜態方面的某些正確性。據說Haskell語言的類型推導系統能使得一個Haskell程序的編譯通過,基本上99%能保證程序的正確性。
但大部分OOP語言,類型的正確只是個底子。一個函數在運行時,依賴於該函數的假設條件是否滿足,一個函數執行應該是怎樣的狀態,執行後應該是怎樣的狀態,一個健壯的函數代碼可能需要寫很多前置條件檢查和後置狀態檢查。
但是檢查的方式有兩類,一類是運行時可能會出現的條件,但不算錯誤,此時需要在函數內部寫條件判斷語句檢查,另一類是運行時調用該函數前後不應該出現的情況,此時需要做的是單元測試。從這個角度來說單元測試是類型安全的延伸,類型是一種對語義的限制,由於運行時語義的組合爆炸,導致不能通過語言內置的類型規則檢查所有的語義限制,所以只好用單元測試來做補丁。
但是,單元測試又有兩種類型:侵入型和外置型。所謂侵入型是指在函數內部做的各種Assert、Check,這些代碼只會在Debug版或者日誌版有效,在發布版裏不會出現。而外置型則是指為一個模塊單獨建立測試工程,在測試工程裏為每個函數每個類專門編寫測試函數,測試函數和類的各種可能的調用(測試用例),只有這些測試都通過了,這個模塊的健壯性才得到一定程度保證。
但是,正如構建之法書中所言,100%的單元測試覆蓋也不能保證消滅所有BUG。譬如我自己日常開發裏,最麻煩的莫過於多線程的BUG調試,由於很多邊界條件依賴於線程的執行序才能重現,這部分很多時候都需要靠人肉分析,定位,甚至有時只能靠細致的code review,但一般來說如果能重現,那就離解決不遠。
而修復了BUG後,還需要做回歸測試。以前有人說:修復一個BUG引入了3個新BUG,最後一個BUG修復後才把最開始的那個BUG修復,所以回歸測試是一種BUG Review。
以上兩部分是根據這章首先引入的兩個部分,接著是性能分析。有一句話說過早的優化是一切罪惡的根源。而優化往往是需要以數據說話的,使用性能優化工具查找性能熱點,然後再有的放矢的去優化。我覺的不過早優化也有利於工程,早期應該關註代碼的良好設計(譬如後面結對編程裏會引入的編程規範、代碼風格等),以及代碼的層次設計(無論是類間還是模塊間)等。
## PSP(Personal Software Process)
我很喜歡PSP這個概念,特別是Process。一個人的技能是靜態的,但Process強調過程性,我們的日常開發也是過程性的。在一個開發過程中,會產生很多指標,這些指標可以用來做自我衡量。當然,在沒有工具支持的情況下,需要這麽做的程序員擁有良好的做記錄的習慣,我想大部分程序員不會每天給自己做PSP指標記錄,如果是實驗室裏的科學家,倒是有可能每天對自己餵養的小白鼠做個種定時記錄。但科學告訴我們,準確和記錄產生的數據會說話。所以,也許在一定粒度上定期自己Review自己的PSP數據的,也應該是一個不錯的方式。當然,牛逼的程序員是不需要靠間斷自我采樣來提高的,大部分程序都宣稱自己是牛逼的,所以PSP的真實被顯性使用應該很少。
過程,意味著不是一個步驟就完成的。我認為此處依然是邊界識別問題。我們做一件事,有很多個步驟,我們可能高度耦合(非線性)在一起就做了。但拆開的話,應該會有好幾個過程,我認為有效的方式未必要刻意把這些過程拆開去做,但一定要有邊界識別的感知能力。我做,我能理解我此時時刻做的是屬於某個過程的東西,我再做,時間上雖然是連續的,但它屬於某個其他過程。
## 基本、高級和擴展
wc程序的基本需求、高級需求、擴展需求。每個基本需求都要做麽?是的!每個高級需求都要做麽?每個擴展需求都要做麽?我相信很多學生對為什麽要思考和提出高級需求、擴展需求是模糊的。我的看法是,高級需求和擴展需求,是為了後續的叠代。一個軟件,會有多個不同階段的開發周期,譬如一周一次叠代。那麽基本、高級、擴展需求是一個初步指南,每個開發周期不宜過長,但在一個叠代周期內要盡量做到按時高質完成對應要做的功能(也就是需求裏所提出的)。這樣,這個軟件的整個長期開放周期會有一個後勁,當然,需求的變動是正常的,在叠代周期的過程中,根據用戶反饋、開發進度調整需求也是要應對的。
參考幻灰龍的讀書筆記
軟工Chatper four