《代碼大全》第二版--第二部分

第二部分:建立高質量代碼

    第五章:軟件構建中的設計

     5.1 設計

        在編碼前進行,好比畫圖,畫xml,想好邏輯怎麼作,新增哪些數據結構,命名;算法

        設計可能會考慮不周,而且設計過程是很是艱難的,會犯一些錯誤 ,可是在設計階段犯錯的代價遠低於編碼階段;數據庫

        設計是易變的;編程

    5.2 設計的重要目標:管理複雜度

        複雜度是設計的重要指標之一,現代軟件一般比較龐大,一我的的腦力遠遠不能裝下這麼多邏輯和設計。好的設計會將整個系統按照邏輯關係劃分爲子系統,在將子系統分解,最終劃分爲一個個小的在咱們大腦記憶範圍內的功能點,咱們可以較容易的理解這些功能點,再將功能點聯繫起來,由小到大的理解整個系統。下降系統複雜度,每一個最小集複雜度達到最小。設計模式

        目標:最小複雜度,易於維護擴展、耦合性, 重用,移植,層次性。數據結構

    5.3 分層設計

        橫向和縱向上的設計,縱向上分層設計,從系統->子系統->模塊->類->子程序。縱向上的設計有自上而下和自下而上,自上而下的好處是相對比較容易,從大局逐步剖析到細節,很差的地方是局部變化因素對上層影響較大,由於是固定好上層,若是下層變化須要影響到上層,會發生連鎖反應,自上而下設計,一般自下而上的開發,先搭建基礎組件。  自下而上的設計好處是先把影響最大而且最基礎的部分設計好,在設計高層,高層能夠隨意更改而不至於影響下層,這樣設計的難點在於要時刻和最終目標對齊,和最終總體目標對齊,不然容易走偏或者設計了一些多餘的用不上的底層接口。ide

        橫向模塊之間的交互必定要注意接口之間的通訊限制,若是沒有限制,邏輯上其實就是一個接口,並無分開,模塊間要作到功能解耦,只經過小部分暴露的接口作交互,並且某一模塊不能和太多模塊打交道,好比數據庫訪問模塊就只和數據庫打交道,而且向外暴露接口,在數據庫變化的時候只須要適配接口就好了。函數

    5.4 設計來源(啓發式)

        將現實生活中的問題抽象成設計中的元素工具

        將生活中的類似實體抽象出共同的抽象特徵性能

        採用信息隱藏、測試

        區分系統易變和不易變部分。找出一個最小的不易變部分,逐步擴大這個部分並檢測這個部分的易變性。直到易變性突破了某個可接受範圍,那麼外邊的更大範圍就是易變的。

        適當採用現有的設計方法:常見的設計模式

    

        補充:幾個代碼設計的原則:

            單一職責原則SRP: 就一個類而言,應該僅有一個引發它變化的緣由

            開放封閉原則ASD:類,模塊,函數等等應該是能夠擴展的,可是不可修改的

            裏式替換原則LSP:全部引用基類的地方必須透明的使用其子類的對象

            依賴倒置原則DIP:高層模塊不該該依賴低層模塊,兩個都應該依賴於抽象。抽象不該該依賴於細節,細節應該依賴於抽象    

            迪米特原理LOD:一個軟件實體應當儘量少地與其餘實體發生相互做用

            接口隔離原則ISP:一個類對於另外一個類的依賴應該創建在最小的接口上。

    

        總結:設計是個很重要的過程,而且須要大量的工做經驗,不只須要有全局觀,還有關注具體模塊功能的實現可行性,須要拆分目標系統,下降複雜度,須要關注的點不少,評估衝突妥協。這塊後面有相關經驗了再回來補充筆記。(普通開發人員在編碼以前也要作se給出的設計進行思考,看有沒有更優秀的方案,實現過程的邏輯僞代碼也最好寫一寫,畫一畫邏輯流程圖)

 

    第六章:能夠工做的類

        6.1 類的基礎:抽象數據類型ADTs

            抽象數據類型是指一些數據以及對這些數據所進行的操做的集合。

            類 = ADTs + 繼承 + 多態

        6.2 良好的接口類型

            類的接口應該展示一致的抽象層次:一個類僅實現一個ADT(單一職責);建立類時明確要實現的抽象是什麼,類中的服務也就是方法一般是成對的。

            類中的數據應該作好封裝,只須要暴露部分以表達這個類的屬性特徵的接口(信息隱藏), 讓調用方針對接口編程而不是去看接口中的具體實現來編程。

             《抽象單一,職責單一,服務單一》

        6.3 有關設計和實現的問題

            1. 包含:has a的關係, 類中的成員數據,能夠聲明也能夠繼承得來,最好不要超過7個成員變量

            2. 繼承: is a 的關係;繼承能夠重用父類的數據和成員,繼承體系不能過多,過多意味着複雜度太高,最好不超過6層。注意繼承的權限:private,protected,public; 避免菱形繼承。

                繼承規則: 若是須要共享數據,則將這部分數據提取出來造成一個數據結構; 若是須要共享行爲,將這些行爲提取出來造成一個基類; 若是都要共享,提取基類並將數據結構填充到基類中去。

                基類控制接口:繼承 

                本身控制接口:包含(包含字段,包含方法,包含對象)

            3.構造函數: 最好在構造函數裏進行成員初始化動做,構造函數要注意深淺拷貝

        6.4 建立類的緣由

            1. 爲現實世界中的對象建模

            2. 爲抽象的對象建模(好比物體的形狀能夠創建一個基類)

            3. 爲了下降複雜度,創建多個類,類之間有相互交互; 類中能夠隱藏信息,好比一個具體的複雜算法或者一個協議,隱藏在類中,對類外暴露接口便可。

            4. 減小參數的傳遞:對於多參數的函數,若是將這些參數封裝成一個類,直接能夠傳遞一個類的對象。

            5. 代碼可重用,將重用的部分代碼封裝成一個類,其餘個性化的類能夠繼承這個父類來獲取那些公用的方法; 

            6. 首先作好系統分析工做,將動態的和非動態的數據分別放到不一樣的類中,動態的數據組成的類能夠隨時變化。

            應該避免的類:萬能類(上帝類), 可有可無的類(將這些類降級或者合併到其餘類中), 類的命名不要使用動詞(若是類中只包含動做而沒有具體數據對象,這個類一般爲一個工具類,名字必定要使用名詞)

        

        補充總結:對類的思考,首先類的做用要單一,和函數同樣,職責要單一,類中應該保存的是某一個ADT,對外暴露的接口也應該和這個ADT的抽象等級相同,對接口的暴露也要三思,最後類的名字要想好,用一個名字來表達。

 

    第七章:高質量的子程序

        子程序:routine, 方法:method, 過程:procedure, 宏:macro

        子程序是爲實現一個特定目標而編寫的一個可被調用的方法或者過程。函數有返回值,過程無返回值。

        7.1 建立子過程的正當理由: 下降系統的複雜度,避免代碼重複, 封裝一個複雜的算法或者不易懂的協議之類的過程, 簡化複雜的布爾判斷, 改善性能,確保每一個子程序都比較小

        7.2 在子程序上設計: 着重內聚性,子程序內部操做緊密,子程序對外變現的功能單一;

        7.3 好的子程序名字:描述子程序所作的功能, 不要使用數字如part1, part2 ; 使用動賓短語。

        7.4 子程序能夠寫多長: 50-100 行, 最好在50行之內、函數越短小,功能越單一,可重用性越高

        7.5 如何使用子程序的參數:

                入參在前,出參在後; 使用宏定義來代表結構體中的出入參;參數最好不要超過5個,參數過多的時候能夠考慮封裝成數據結構或者對象的方式傳入,參數命名也很重要; 形參名和實參名儘可能保持一致。

 
#define  IN
#define  OUT    
struct {
     IN  int putIn;
     OUT  int putOut
}  
 

        7.6 使用函數時要特別注意的事情:何時用函數,何時用過程,函數有返回值,過程無返回值,有的時候表示過程的一個函數能夠返回一個值用來表示這個過程是否執行成功。

        7.7 宏子函數與內聯子函數:

            宏子函數中的參數和計算過程必定要用括號包起來,由於宏展開的時候各類符號的優先級可能會出現非定義狀況,

            宏中多條語句的時候要用大括號包起來,防止在if後面緊跟宏時,宏展開只會有一條語句在for, if循環做用範圍內;

            取代宏的手段:const , inline , template, enum, typedef

 
#define club(a)   ((a)*(a)*(a))   // a爲x+1  -->   ((x+1)*(x+1)*(x+1))    
 

        總結:子程序,目的在於下降複雜度,提升可讀性,可靠性,可修復性,可重用性,封裝隱藏信息,對於子程序,要注意內部的內聚性,一個子程序只作一件事情,注意子程序行數儘可能保持在50行內,最後,要給子程序取個好名字,達到從名字就能看出這個子程序完成的功能是啥。

 

    第八章:防護式編程

        子程序不因傳入的錯誤數據而被破壞

    8.1 保護程序免遭非法輸入數據的破壞:檢查全部來源於外部的數據, 決定如何處理錯誤的輸入數據。

    8.2 斷言:開發和維護階段使用,生產代碼不要編譯進去。斷言檢查的是不應發生的狀況,錯誤碼用來檢查不太可能發生的異常場景。

    8.3 錯誤處理技術:返回中立值,換用下一個正確的數據, 返回與前一次相同的數據, 記錄日誌文件,返回一個錯誤碼; 應該具體場景具體分析,對於正確性要求較高的場景應確保正確性,對於健壯性要求較高的時候應着重健壯性。

    8.4 異常:把代碼中的錯誤或者異常事件傳遞給調用方代碼的一種特殊手段。提出異常,交出控制權,或者本身處理異常。高健壯性要求對異常處理要全面。避免在構造函數和析構函數中拋出異常,好比在構造函數中拋出異常,異常以前分配的資源就得不到釋放,這個時候就有內存泄漏問題; 瞭解庫函數的異常狀況。

    8.5 隔離程序,使之包容由錯誤形成的損害: 調用子程序前,先作preCheck動做

    8.6 輔助調試代碼:目的是快速的檢測錯誤

        進攻式編程:在開發階段讓異常顯示出來,而在產品代碼運行時讓它可以自我恢復。

        對於輔助的調試代碼,在正式產品中能夠經過工具或者編譯選項將他們剔除於生產代碼中。

    8.7 肯定在產品代碼中保留多少防護式代碼:若是每一個子程序在正式代碼以前都先運行防護代碼,那麼系統將會變得臃腫,首先第一點,去掉一些細微錯誤的檢查代碼,去掉硬件因素引發的異常代碼,保留讓程序穩妥的崩潰的代碼; 第二,制定個產品規則,對於大多數的子程序,能夠經過調用方來保證數據的正確性,有的子程序內部調用了幾個子程序,這幾個子程序的防護代碼都是一致的,在這些子程序外部進行一次數據校驗後就能夠給多個子程序使用,若是每一個子程序都去作一遍有點多餘,我的以爲大多數子程序的數據正確性由調用方負責比較靈活。

    8.8 對防護式編程的態度:過多的防護式代碼會讓系統變得臃腫和緩慢,增長系統複雜度,可適當根據防護等級決定代碼的編寫。

    總結: 能夠根據具體的項目要求來,有的項目要求調用方負責數據的正確性,有的要求被調用者負責數據的正確性,仍是應該按需來。    

 

    第九章:僞代碼編程過程

        9.1 關於僞代碼:與具體語言無關,能夠當成註釋來寫,儘可能保持與邏輯一致

        9.2 經過僞代碼建立子程序:檢查先決條件;定義子程序要解決的問題:輸入,輸出,功能; 爲子程序命名; 決定如何測試子程序; 搜索重用的代碼; 代碼效率性能考慮; 算法與數據結構的安排;編寫僞代碼;評審僞代碼;編寫代碼;

        總結: 僞代碼能夠當作是具體代碼的稍上層表現,包含了大概的實現過程,又經過簡易的預覽來描述實現過程,在寫代碼以前進行僞代碼編寫能夠提早預估到困難點以及應對方法。

相關文章
相關標籤/搜索