《构建之法》 读书笔记
《構(gòu)建之法》
讀書(shū)筆記
婁雨禛 PB16060356
?
第一部分 關(guān)于結(jié)對(duì)編程的體悟與實(shí)踐
在結(jié)對(duì)編程這一部分我曾講過(guò)很多的注意點(diǎn),比如代碼變量命名風(fēng)格、縮進(jìn)風(fēng)格、注釋風(fēng)格,前后語(yǔ)句次序風(fēng)格,等等。然而這里還有一些新的東西。代碼風(fēng)格這個(gè)老掉牙的話題咱們先擱置不談,而說(shuō)說(shuō)在結(jié)對(duì)編程中同樣重要的其他注意點(diǎn)。
?
代碼復(fù)審
代碼復(fù)審是一門學(xué)問(wèn)。良好而有序的復(fù)審將幫助我們快速排查問(wèn)題和增進(jìn)代碼可讀性,而低劣的復(fù)審則純粹在浪費(fèi)時(shí)間。這里拿我在個(gè)人項(xiàng)目中的代碼舉一個(gè)例子。
?
1 void anothermain(char *fileString) 2 { 3 char *offset = fileString; 4 char move; 5 char temp; 6 7 int count; 8 do 9 { 10 if(temp = *offset) 11 { 12 if(temp >= 32 && temp <= 126) 13 { 14 Characters++; 15 if(temp >= 65 && temp <= 122) 16 { 17 if(temp >= 91 && temp <= 96) 18 { 19 offset++; 20 continue; 21 } 22 else 23 { 24 offset++; 25 word[0] = temp; 26 } 27 } 28 else if(temp >= 49 && temp <= 57) 29 { 30 offset++; 31 while(*offset >= 49 && *offset <= 57) 32 { 33 Characters++; 34 offset++; 35 } 36 while((*offset >= 49 && *offset <= 57) || (*offset >= 65 && *offset <= 90) || (*offset >= 97 && *offset <= 122)) 37 { 38 Characters++; 39 offset++; 40 } 41 continue; 42 } 43 else 44 { 45 offset++; 46 continue; 47 } 48 } 49 else 50 { 51 if(*offset == 10) 52 enterNum++; 53 offset++; 54 continue; 55 } 56 } 57 else 58 break; 59 if(temp = *offset) 60 { 61 if(temp >= 32 && temp <= 126) 62 { 63 Characters++; 64 if(temp >= 65 && temp <= 122) 65 { 66 if(temp >= 91 && temp <= 96) 67 { 68 offset++; 69 continue; 70 } 71 else 72 { 73 offset++; 74 word[1] = temp; 75 } 76 } 77 else if(temp >= 49 && temp <= 57) 78 { 79 offset++; 80 while(*offset >= 49 && *offset <= 57) 81 { 82 Characters++; 83 offset++; 84 } 85 while((*offset >= 49 && *offset <= 57) || (*offset >= 65 && *offset <= 90) || (*offset >= 97 && *offset <= 122)) 86 { 87 Characters++; 88 offset++; 89 } 90 continue; 91 } 92 else 93 { 94 95 offset++; 96 continue; 97 } 98 } 99 else 100 { 101 if(*offset == 10) 102 enterNum++; 103 offset++; 104 continue; 105 } 106 } 107 else 108 break; 109 if(temp = *offset) 110 { 111 if(temp >= 32 && temp <= 126) 112 { 113 Characters++; 114 if(temp >= 65 && temp <= 122) 115 { 116 if(temp >= 91 && temp <= 96) 117 { 118 offset++; 119 continue; 120 } 121 else 122 { 123 offset++; 124 word[2] = temp; 125 } 126 } 127 else if(temp >= 49 && temp <= 57) 128 { 129 offset++; 130 while(*offset >= 49 && *offset <= 57) 131 { 132 Characters++; 133 offset++; 134 } 135 while((*offset >= 49 && *offset <= 57) || (*offset >= 65 && *offset <= 90) || (*offset >= 97 && *offset <= 122)) 136 { 137 Characters++; 138 offset++; 139 } 140 continue; 141 } 142 else 143 { 144 offset++; 145 continue; 146 } 147 } 148 else 149 { 150 if(*offset == 10) 151 enterNum++; 152 offset++; 153 continue; 154 } 155 } 156 else 157 break; 158 159 if(temp = *offset) 160 { 161 if(temp >= 32 && temp <= 126) 162 { 163 Characters++; 164 if(temp >= 65 && temp <= 122) 165 { 166 if(temp >= 91 && temp <= 96) 167 { 168 offset++; 169 continue; 170 } 171 else 172 { 173 offset++; 174 word[3] = temp; 175 for(count = 4; isLetterOrNum(*offset); offset++, count++) 176 { 177 Characters++; 178 word[count] = *offset; 179 } 180 word[count] = '\0'; 181 NumOfWords++; 182 SolveTheWord(word); 183 } 184 } 185 else if(temp >= 49 && temp <= 57) 186 { 187 offset++; 188 while(*offset >= 49 && *offset <= 57) 189 { 190 Characters++; 191 offset++; 192 } 193 while((*offset >= 49 && *offset <= 57) || (*offset >= 65 && *offset <= 90) || (*offset >= 97 && *offset <= 122)) 194 { 195 Characters++; 196 offset++; 197 } 198 continue; 199 } 200 201 else 202 { 203 offset++; 204 continue; 205 } 206 } 207 else 208 { 209 if(*offset == 10) 210 enterNum++; 211 offset++; 212 continue; 213 } 214 } 215 else 216 break; 217 218 219 } 220 while(*offset != '\0'); 221 }?
這是我在進(jìn)行匆忙的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)型時(shí)書(shū)寫(xiě)的非常潦草的代碼。雖然潦草,但還是包含了我的很多想法,比如,進(jìn)肯能利用“流式操作”減少機(jī)器的循環(huán)流程,提高代碼效率,盡管這樣會(huì)使代碼變得非常難讀。很快,我就意識(shí)到了,這樣的操作雖然節(jié)省了機(jī)器執(zhí)行的時(shí)間,卻大大浪費(fèi)了我自己的時(shí)間。這段代碼的可讀性非常的差,以至于我自己常常被繞暈。其中雜亂的命名方式就不多說(shuō)了,簡(jiǎn)直不忍卒讀。由于是一次個(gè)人作業(yè),又到了馬上要提交的截止日期,我只關(guān)心這段程序的執(zhí)行結(jié)果是否正常(作用是判斷是否為一個(gè)單詞并進(jìn)行儲(chǔ)存),因此在最后輸出結(jié)果正確之下,我就沒(méi)有再對(duì)它進(jìn)行優(yōu)化和修改。
而如果這換成一次結(jié)對(duì)編程,將會(huì)怎樣呢?
是的,代碼復(fù)審就登場(chǎng)了。那么下面,我就結(jié)合這個(gè)例子,簡(jiǎn)要說(shuō)說(shuō)應(yīng)該怎樣利用復(fù)審,改進(jìn)代碼。
?
Step1:“自己改+你說(shuō)我看”:不符合共同協(xié)定的編碼風(fēng)格的地方,一律修改
我們需要在一開(kāi)始就商定好代碼風(fēng)格,比如哪些地方需要換行,變量的命名規(guī)則等等。
然后,先過(guò)自己這一關(guān)——自己對(duì)代碼進(jìn)行修改。比如在上面的例子中,函數(shù)名為anothermain,這是什么鬼意思?這種根本不知其所以然的命名,必須根除。還有像enternum這樣的變量,也會(huì)讓人摸不著頭腦,應(yīng)該改為characterNum這種規(guī)范的命名。
在自己認(rèn)為沒(méi)有大毛病之后,還需要對(duì)方對(duì)自己進(jìn)行挑錯(cuò)。在這里,挑錯(cuò)要注意一個(gè)原則:不要在雞蛋里挑骨頭,要本著找出有意義的錯(cuò)誤的原則,認(rèn)真查錯(cuò)。比如,“這樣的函數(shù)命名會(huì)不會(huì)和以后的命名沖突,從而引起歧義?”
Step2:探討潛在的邏輯問(wèn)題,預(yù)防“邏輯災(zāi)害”的發(fā)生
上面這段代碼的邏輯完全是給機(jī)器看的,對(duì)于人類閱讀者來(lái)說(shuō),幾乎看不下去。這時(shí)候,雖然程序在執(zhí)行的時(shí)候結(jié)果沒(méi)有問(wèn)題,代碼還是得改。把從頭執(zhí)行到尾的“流式操作”改為稍微慢一些卻容易閱讀地多的代碼,我們可以增加一些執(zhí)行很小功能的函數(shù),然后反復(fù)調(diào)用它們。這樣,將得到容易閱讀得多的代碼。
Step3:修改完后也不松懈,繼續(xù)進(jìn)行經(jīng)驗(yàn)總結(jié)
總結(jié)這個(gè)步驟,是讓我們“事半功倍”的不二捷徑。它不容忽視
?
在修改之后,代碼變成了下面這樣。
1 void WordCheck(FILE *fp) 2 { 3 bool stop = false; 4 bool isEmpty = false; 5 char ch; // ch gets the character one by one 6 short check = 0; // to check if the first four characters are letters 7 short i = 0; 8 9 ch = fgetc(fp); 10 if(ch == EOF) 11 isEmpty = true; 12 13 for(; !stop; ch = fgetc(fp)) // if it is not the end of the text 14 { 15 if(ch >= 32 && ch <= 126) 16 characterNum++; 17 if(ch == '\n') 18 lineNum++; 19 20 if(check < 4) // to check the first four characters 21 { 22 if(ch == EOF) 23 { 24 stop = true; 25 if(isEmpty == false) 26 lineNum++; 27 } 28 29 else if(IsLetter(ch) == true) 30 { 31 ++check; 32 tempWord[i] = ch; 33 ++i; // search for the next 34 } 35 else 36 { 37 i = 0; 38 check = 0; 39 ClearTemp(); 40 } 41 } 42 else // first four characters are all letters, ready to store 43 { 44 if(IsSeparator(ch) || ch == EOF) // have met a separator, store the word 45 { 46 i = 0; // roll back to the beginning in the next search 47 check = 0; // roll back to the beginning in the next search 48 49 wordNum++; // have found another word 50 StoreWord(); // store the word 51 ClearTemp(); // prepare for the next search 52 53 if(ch == EOF) 54 { 55 stop = true; 56 if(isEmpty == false) 57 lineNum++; 58 } 59 } 60 61 else // have not met a separator, keep searching 62 { 63 tempWord[i] = ch; 64 ++i; // search for the next 65 } 66 } 67 } 68 }?
根據(jù)運(yùn)行結(jié)果顯示,修改后的代碼運(yùn)行時(shí)間是原來(lái)的兩倍,但我們卻獲得了清爽得多的函數(shù)。這應(yīng)該是一個(gè)好的結(jié)果,至少在對(duì)速度的要求不高的情況下。
然而,如果我們進(jìn)行過(guò)程序優(yōu)化,就會(huì)發(fā)現(xiàn),這是一個(gè)很令人詫異的結(jié)果——代碼的運(yùn)行時(shí)間變成了原來(lái)的兩倍!這究竟是怎么回事呢?
原來(lái),在新的函數(shù)中,我采取了反復(fù)調(diào)用子函數(shù)的措施來(lái)精簡(jiǎn)代碼。而在我的子函數(shù)中,我為了繼續(xù)精簡(jiǎn)代碼,又套用的新的子函數(shù),這里進(jìn)行了兩次的套用。而在判斷是否為一個(gè)單詞并進(jìn)行儲(chǔ)存的時(shí)候,這種層層調(diào)用的次數(shù)是超乎想象的。正是這里頭的反復(fù)嵌套,大大拖慢了程序的運(yùn)行速度。
接下來(lái)是進(jìn)行修改,在不損失代碼簡(jiǎn)潔性的前提下進(jìn)行代碼優(yōu)化,其中主要是性能優(yōu)化。我首先要做的是把兩層的函數(shù)嵌套改為一層。
注意,在這里我并沒(méi)有把子函數(shù)放上來(lái),是出于閱讀的簡(jiǎn)潔性和直觀性——直接把我的實(shí)踐結(jié)論告訴大家,而不是用代碼和大家繼續(xù)兜圈子。
通過(guò)將兩層的函數(shù)嵌套改為一層,我的子函數(shù)從原來(lái)的16行變成了42行,可以說(shuō)復(fù)雜了不少,但接下來(lái)我們看一下運(yùn)行效率。
代碼的運(yùn)行時(shí)間大約是最初運(yùn)行時(shí)間的 1.4 倍。
這個(gè)結(jié)果比第一次優(yōu)化的結(jié)果好了不少,但沒(méi)有達(dá)到本次代碼優(yōu)化的最終目的——在不損失性能的前提下進(jìn)行感官上閱讀體驗(yàn)的提升。當(dāng)然,我也已經(jīng)知曉了代碼變慢的原因——函數(shù)嵌套和函數(shù)調(diào)用花去了太多無(wú)用的時(shí)間,接下來(lái)的優(yōu)化步驟也明晰了。
?
轉(zhuǎn)載于:https://www.cnblogs.com/RainLou/p/9008681.html
總結(jié)
以上是生活随笔為你收集整理的《构建之法》 读书笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: Page_Load的问题
- 下一篇: 20155202《网络对抗》Exp9 w
