1. 程式人生 > >結對編程收獲

結對編程收獲

分塊 減少 之一 計算機 人做 身邊 知識 ace 轉化

《結對編程收獲》

#關於類與C++

  雖然自學過一點C++,但這是第一次付諸實踐。C++的一個關鍵在於創建對象。所以對於這個項目,我們該創建哪些對象便是一個重大的難題。剛開始看C++的例子的時候,感覺對象的選擇和創建都是顯而易見的,但事實並非如此。當我們面對的是一個比較抽象的題目的時候,或者說沒有比較清晰的現實世界中的對象被映射的時候,抽象這件事就變得難了起來。這並非說抽象這件事本身很難,而是指怎麽抽象。抽象的選擇有很多,但如何抽象,可以使得對象與對象之間的耦合不要太緊密,如何讓對象與對象的概念的區分是清晰的,如何讓對象的方法之所以隸屬於這個對象顯得較為合理,都是對象的建立需要考慮的。

  考慮到我們是用表達式二叉樹來儲存一個表達式並檢查其是否重復,最後我們選擇了建三個類,分別是setting,設置類,generate,產生類,這也是最外層的類,結點類,node類,負責計算和數字運算符表達和轉化。其中setting類一開始我們設定為一個結構體,也就是它的信息是可以共享到整個代碼段的,但後來我們發現,我們有時候需要基於這些數據做一個獨立的計算函數,並且有時候我們需要強制修改參數(我們在生成結點時有時候要修改這個)於是我們將其修改成一個類。在修改過後不久,我便些許意會了類的好處和意義,那就是generate和node不需要具體了解setting內的數據結構,這在我們後來修改setting的結構的時候帶來了很大的好處,由於其他類不需要了解setting的結構,故generate和node方法不需要做改動,只需要在setting的方法做改動即可。這真的大大降低了程序出錯的概率,而這種錯誤有很多是編譯器並沒法發現的,因為根本就沒有語法錯誤。這是維護代碼很重要的方法,設想一下,如果setting.operator_type在代碼的各個地方暴露,那麽當我對setting.operator_type的定義進行了修改,我就需要在整個代碼段裏尋找setting.operator_type然後根據我自己的設定進行修改,而如果利用類進行信息封裝,我只需要修改setting::getOperator_type()。

  我們對這三個類的設計使得setting 被generate和node調用而不調用其他的類,node只調用setting,並只被generate和node調用,generate 調用node和setting而不被其他類調用。

類使得整個代碼更有合理性,更容易進行測試,使得模塊代碼比較不容易出錯,比如說,num2string()這個函數,我們單獨對它測試,並根據要求的更新對其進行修改的思路顯得特別清晰。但分塊也有一個問題,那就是函數與函數的銜接有時候沒有考慮好,這啟發我們在單獨測試或者編寫這個函數時,要全面的考慮這個函數可能被調用的所有情況,以及應用的所有情景,當然我們不可能事事都不遺漏,所以再把所有代碼全部寫好的時候,按照代碼執行邏輯順序再把整個程序過一遍,可以發現很多考慮不周的問題。

  不得不說第一次嘗到了使用類的甜頭,但顯然這種層次的使用還非常的低級,很多問題顯而易見,比如說類的方法過多,這意味著我們可以創建更多的類,或者創建子類。並且我們並沒有用到繼承,泛型等應用,由於不熟悉C++的原因,我們對其的使用還非常的片面。這還需要大量的練習。

  我們對分數,小數和整數進行了形式上的統一,然後將運算符重載。這讓我又一次體會到了封裝的意義。事實上不封裝的代碼量可能是差不多的。但這使得代碼看起來思路更為清晰和簡潔。

  我們在用string類的時候不知道為什麽就莫名報錯了,兩個人折騰了好久,以為是頭文件包含的問題,最後問別人之後,才知道是沒有寫using namespace std;真的讓人痛心疾首,這告訴我們基本功的重要性,同時也告訴我們身邊有大佬有多麽的重要,並且遇到問題要及時向別人請教。

#關於封裝與API

  封裝雖然麻煩,比如說我們要寫很多set(),get(),函數但是這有利於代碼維護,這就不多說了。而提供接口是這次編程的key,我們要如何提供接口而使得UI組不需要關心我們內部的具體結構,不需要關註core組的實際細節。這次的作業讓我們

  對於輸入,我們想的辦法就是用一個函數來接受他們的輸入,也就是參數作為函數參數傳進來。這樣子他們只需要調用這個函數就行就可以進行參數的傳遞,而不需要知道參數是如何被儲存的。

  但事實上這有一個問題,鑒於大家寫的都是C++,不存在語言的切換問題,這樣傳參應該說是最簡單的。但是假設有人不是用C++寫的就比較麻煩,所以如果都用Ascaii碼就不存在這種問題,就如被提到的xml,但是鑒於沒有時間再學新的東西(時間實在不夠),我們就采用了函數傳參的方式。

  對於輸出,我們采取了兩種輸出模式,一種是內存法,一種是文件法,共UI選擇。文件法是最開始寫的,是因為一開始就有UI說要讀文件,後來在對接的時候有UI說需要用內存讀,於是我們又寫了內存版的對接,文件輸出就不需要輸出對接代碼,而內存版我們給了兩個指針,事實上這也是建立在我們雙方都使用C++的基礎上。

  關於對接,一開始沒有商量好接口標準,是一個讓人很頭大的問題,一千個組會產生一千種API。這啟發我們在做團隊項目工作前,事實上不管有沒有人組織進行確立API的討論,討論接口問題都是必須的,作為core,我們要提前了解UI需要什麽樣的接口。雖然接口這種東西是獨立於核心代碼的一種東西,我們可以在不改動核心代碼的基礎上修改接口,但頻繁的修改接口是沒有意義的。

  正如《程序員的自我修養》裏所說,接口的另一個層面的含義是協議,接口是一種約定,所以,這是core和UI組需要事先商量好的,我想這是通過這個項目我們學到的東西。有時候我們疏於溝通,是怕麻煩,但事實上不溝通會造成更大的麻煩。

#關於Debug技能的收獲

  事實上雖然我知道基本的斷點設置,單步調試,條件斷點等Debug方法,面對“程序終止運行”時,或者程序陷入死循環,我都不知道從何入手。好在隊友的Debug速度很快,看了幾次調試過程之後,我大概也能夠跟上Debug的思路,能夠比較快的找到問題。希望以後不再畏懼調試程序。我想這是結對編程的好處之一把,就是相互學習。並且知道了有時候程序出錯是不會報錯崩潰的,有時候就直接停在了那裏。這應該是由於調用了某些系統函數的原因,比如說申請內存,然後內存申請完了,無法申請,於是程序就停止運行,這時候無法知道程序到底在哪裏崩潰了,我們沒法利用IDE提供的錯誤信息,這就增加了Debug的難度。

  如果我們無法知道是到底在第幾次調用出了問題,那麽我們可以采取命中次數(或條件斷點)的形式,用二分法的方法比較快地查出哪次調用出錯。

  並且在產生隨機數的時候,由於電腦的隨機數是偽隨機數列,所以為了方便Debug,可以先不srand(),用計算機一開始的默認的種子,等到程序調試基本沒問題後再加上srand()。

#結對編程的優點

事實上我們並沒有在每一部分代碼都做到一個人看的同時一個人寫。但是因為我們是在一塊兒寫的代碼,在每一塊代碼寫之前,我們都會進行討論,所以我們對對方寫的代碼的邏輯也比較清楚,所以我們對對方的代碼上理解是比較快的,並且我們會對對方的代碼做一次檢查,順帶理解。所以我們的Debug工作不管由誰進行,錯誤在誰寫的代碼上發生,都可以進行程序的修改。我想這要感謝隊友的合作。

兩雙眼睛確實來的比一雙眼睛明亮,而有時候很明顯又很低級的錯誤自己發現不了會被隊友一眼看出,這大大的降低了Bug的概率,並且兩個人商量出來的算法或者結構往往可以互補優化,彼此的知識可以互相補充,並且兩個人做一件事就會比較有信心,是很大地提高了編程的效率。我認為結點編程看起來是兩個人同時完成一個任務,仿佛降低了生產力,但事實上這大大減少了Debug的時間,有時候我們查Bug的時間遠遠多於我們的代碼,並且結對的代碼質量也相對較高,不得不說這個一個非常棒的方法。

#其他

  最後應該就是第一次建DLL,並且了解到什麽是動態鏈接庫,它的作用是什麽。並且自己寫了測試文件,成功調用了dll。

#寫在最後

 雖然很辛苦,但是收獲還是很大的。寫代碼是需要實踐的,我們需要在實踐中學習,不是看書看了就會寫的。雖然很辛苦,但相信沒有UI辛苦,UI要辛苦地跟我們core組對接。還要感謝大佬們,給出建議並幫忙解決了一些看起來很小但確實把我們難住的問題。大概正如鄧老師所說“技術後備團”也是很重要的。還要感謝隊友的理解和支持。

結對編程收獲