結對編程心得---------優秀的隊友是成功的一半
一,結對編程的收獲
1.團隊分工
結對編程作業大部分的時間都是采取共同編寫代碼,即“一個做駕駛員,一個做領航員”,駕駛員負責敲鍵盤,領航員在一側提供建議、檢查錯誤或幫忙搜索相關的資料。
就這次團隊項目而言,我們合作還是很愉快的。清明節的後兩天我和朱池葦同學一起學習了Qt。由於朱池葦同學的工程能力比較強,所以大部分程序都是朱池葦同學作為駕駛員,而我(劉鼎乾)則作為領航員,與朱池葦同學對問題進行討論,查找資料,檢查錯誤等。
2.結對編程的意義
我覺得結對編程確實具有非常重要的意義:
- 一個人編寫代碼,總是有種燈下黑的現象。可能你自己犯的很微小的錯誤,你花了大把大把的時間,還是搞不出來。而另一個人只需要看一下就能指出,這提高了復審效率。
- 可以不斷從別人那裏學習,提高自己的水平。這次結對編程,我從朱池葦大佬那裏學到了很多,比如良好的代碼風格,清晰的編碼思路,以及熟練的調試技巧等
- 通過兩個人的討論,更好的解決問題等。
3.代碼的可讀性
以前數據結構的助教說我的代碼可讀性差,這是為什麽呢,這裏附一段我以前寫過的代碼:
1 unsigned long long a; 2 int b; 3 for(int i=0;i<10000;i++) 4 { 5 cin >> a >> b; 6 switch (b) 7 { 8 case 1: 9 charu(a); 10 break; 11 case 0: 12 chaxun(a); 13 break; 14 default: 15 break; 16 } 17 18 } 19 20 cout << n2 << "/" << n1 << endl; 21 cout << m2 << "/" << m1 << endl; 22 return 0;
a是什麽,b是什麽,n1 n2又是幹什麽的呢?不僅沒有註釋,而且用的變量名也非常不好。
而這本書給了很多提高代碼規範的建議,比如:
- 縮進,一般以4個空格
- 行寬,不多於100個字符
- 用括號清楚地表達優先級
- 斷行與空白的{}行
if (condition1) { Do something; } else { Do something else; }
一般用這種方式會讓人覺得層次清晰,邏輯分明
- 命名!想我之前寫的那段程序,最主要的問題之一就在於沒有命名。我以前覺得命名無所謂,但現在覺得,好的命名要讓人能夠見名知意,這對提高代碼可讀性是重要的,很多程序員為了一個好的名字還想半天。一般可用下劃線來構造很好地命名。
註釋,沒有註釋的代碼讓人讀起來很痛苦,而好的註釋主要是告訴別人這個程序在做什麽,為什麽這麽做,以及需要註意的東西。這樣別人讀起你的代碼才不會很痛苦。
於是乎我們就要問,為什麽我們需要好的代碼規範?為什麽我們需要別人看懂我們的代碼?
這在結對編程中,尤其重要。假如說沒有人看得懂你的代碼,結對編程也就只是紙上談兵了。
4.好隊友的重要性
這次結對編程,我從我的隊友朱池葦同學身上學到了很多。他的工程能力很強,代碼可讀性也很好,而且思路也很清晰。能經常和這樣的隊友結對編程,我突然對能否最終調試成功充滿了信心。這說明了,結對編程的隊友有多重要。
二、對接的收獲
UI主要工作量集中在設計界面,代碼量並不大,大概在300行左右(除開Qt Creator生成的代碼,自動生成的代碼在1500行左右)。但是對接的時候,各組Core的API甚至邏輯功能的差別,給我們帶來了巨大的麻煩。
首先是邏輯層面,有的組要求加減、乘除分別綁定,而有的組要求可以單獨支持任一種運算;有的組要求小數和分數不能同時存在,而有的組要求一個式子裏既有小數又有分數,這樣的情況下,同樣的UI界面不可能滿足所有Core的要求(比如QRadioButton選項互斥,而QCheckBox可以復選,不支持復選的 core用了QCheckBox,就會造成非法輸入),也許需要根據不同的core發布不同版本的UI。但這個工作量顯然太大了。
另外則是每個組的接口參數不同,需要編寫不同的接口轉換代碼,而且代碼量的高低,很大取決於API的好壞。本次所有Core中的API主要分為三個檔次:
第一檔:接口函數少,傳參合理,可以直接把UI獲取的用戶作為函數參數。這樣的接口,對接代碼量是最小的。
這是第一檔的接口代碼:
//setting
set_setting(oNum-1,ui->oUppRange->value(),ui->precisionSpin->value(),ui->fraction->isChecked(),ui->real->isChecked(),ui->mulDiv->isChecked(),ui->power->isChecked());
// generate one question and one answer (loop this)
std::string *question = new std::string();
std::string *answer = new std::string();
generate(&question,&answer);
ui->questionBrowser->setText(QString::fromStdString(*question));
ui->answer->setText(QString::fromStdString(*answer));
第二檔:接口函數多,傳參合理。這是本次大部分組core的API所處在的檔次,這種API把setting的每一個參數分別設置為一個函數,需要單獨傳輸,但是參數設計合理,可以直接將用戶輸入作為函數參數。這樣的接口,setting的代碼量較大,有時需要做用戶輸入到core接口的轉換(例如“是否支持小數/分數”的參數是0,1,2,但UI得到的數據是選擇框是否被選中的布爾型,因此要先做邏輯判斷,再進行轉換)。
這是第二檔所需要的接口轉換代碼(為了避免可以看出是哪一組,我對函數名作了修改):
//setting
core.setQueNum(ui->qNum->value()); core.setDataNum(ui->oNum->value()); core.setRange(ui->oUppRange->value());
core.setAccuracy(ui->precisionSpin->value());
//set operator type if(((ui->mulDiv->isChecked())&&(ui->power->isChecked()))==true) core.setOprType(3); else if(ui->mulDiv->isChecked()==true) core.setOprType(2); else core.setOprType(0);
//set operand type if(ui->fraction->isChecked()&&(!ui->real->isChecked())) core.setDataType(2); else if(ui->real->isChecked()&&(!ui->fraction->isChecked())) core.setDataType(1); else if((!ui->real->isChecked())&&(!ui->fraction->isChecked())) core.setDataType(0);
//generate all questions
question=core.getQue();
answer=core.getAns();
ui->questionBrowser->setText(QString::fromStdString(question[0]));
ui->answer->setText(QString::fromStdString(answer[0]));
//loop this (i is defined in the header file)
if(i<totalQNum)
{
ui->questionBrowser->setText(QString::fromStdString(question[i]));
ui->answer->setText(QString::fromStdString(answer[i]));
i++;
}
第三檔:最差的一檔,函數多,而且參數設置不合理。
某一組,曾經將所有的函數的傳入參數全部設置為了字符串!!!這意味著什麽?意味著UI組要將所有得到的整形、布爾型先進行邏輯判斷,再轉化成對應的字符串,最後再調用接口函數。而當我們為這個組的接口編寫了復雜的轉換代碼並實現了之後,這個組將API改回了直接傳參的模式。(就不貼代碼了)。
(這裏對號入座太簡單,在這裏對那一組說聲抱歉,但是我實在很想吐槽這一點)
不難想到,實際上這種輸入為字符串的方式一定也給core組增加了工作量,那麽他們為什麽會這樣做?
我可以理解這個core組的想法,可以說出發點是非常好的,他們希望自己的接口更加“易懂”,對於最終用戶,什麽東西最好懂?當然是自然語言!比如,字符串“precision”代表了“精度”,“+-*/”代表“加減乘除”,函數的功能是簡單易懂的。但是他們忽略了一點,這種考慮應當是相對於最終用戶,也就是使用這個軟件的人而言的,而不是相對UI編寫者而言的。對於UI編寫者,只需要定義清晰易懂的變量名、提供能夠理解的API文檔,就足夠了。而如何用自然語言去通俗易懂地表述輸入要求,應當是我們UI組所考慮的內容。
結對編程心得---------優秀的隊友是成功的一半