[软件工程基础]结对项目 数独程序扩展
(1)在文章開頭給出Github項目地址。(1')
項目地址:https://github.com/JerryYouxin/sudoku
(2)在開始實現(xiàn)程序之前,在下述PSP表格記錄下你估計將在程序的各個模塊的開發(fā)上耗費的時間。(0.5')
| Planning | 計劃 | 30 | 60 |
| · Estimate | · 估計這個任務需要多少時間 | 30 | 30 |
| Development | 開發(fā) | 1020 | 1540 |
| · Analysis | · 需求分析 (包括學習新技術) | 120 | 240 |
| · Design Spec | · 生成設計文檔 | 30 | 40 |
| · Design Review | · 設計復審 (和同事審核設計文檔) | 60 | 120 |
| · Coding Standard | · 代碼規(guī)范 (為目前的開發(fā)制定合適的規(guī)范) | 30 | 60 |
| · Design | · 具體設計 | 60 | 90 |
| · Coding | · 具體編碼 | 600 | 800 |
| · Code Review | · 代碼復審 | 60 | 90 |
| · Test | · 測試(自我測試,修改代碼,提交修改) | 60 | 100 |
| Reporting | 報告 | 50 | 80 |
| · Test Report | · 測試報告 | 50 | 100 |
| · Size Measurement | · 計算工作量 | 5 | 10 |
| · Postmortem & Process Improvement Plan · | 事后總結, 并提出過程改進計劃 | 30 | 30 |
| 合計 | 1215 | 1770 |
(3)看教科書和其它資料中關于Information Hiding, Interface Design, Loose Coupling的章節(jié),說明你們在結對編程中是如何利用這些方法對接口進行設計的。(5')
信息隱藏,對于面向對象的程序設計而言,信息隱藏對于將對象封裝,避免外來操作對其進行盲目乃至錯誤的操作,在具體實施上主要是通過只提供接口讀入和得到結果,避免其中的具體實現(xiàn)的過程對外暴露,從而導致不安全的訪問。我們在合作代碼的時候,盡量只給互相提供相互之間所需要的接口,避免因為看到內部代碼而導致的過度操作,以提高程序的安全性與健壯性。
interface design 和loose coupling實際上在很大程度上提高了我們工作的效率。這兩者都使我們能夠專注于自身所要完成的那一部分代碼中,使得個人的工作具體化,簡單化,而不要求我們必須具有著眼整個完整項目的能力,也避免了我們在擁有這種能力之前過于好高騖遠或者不敢下手,從而浪費時間,松耦合和接口設計使得我們能夠專心做好自己的事情,不必過度分心從而導致的效率下降,同時,只提供接口和松耦合的方式也使得我們彼此的代碼不互相干擾,從而導致雙方共同的困難。
4)計算模塊接口的設計與實現(xiàn)過程
計算模塊接口的設計與實現(xiàn)過程
計算模塊接口如下(Core類中的成員函數(shù)):
其中generate(int number, int result)用回溯法進行終局的搜索(可以參考我的個人項目的博客)。在實現(xiàn)生成只有唯一解的數(shù)獨時,先用上述終局生成函數(shù)生成數(shù)獨終局,再進行挖空,每次挖空后用解數(shù)獨函數(shù)來求解數(shù)獨,該函數(shù)可以返回數(shù)獨解的數(shù)量,由此可以判斷是否為唯一解。如果不是唯一解的數(shù)獨則回溯不挖空,并再查看下一個空是否可以挖(這是一個遞歸)。代碼如下:
bool Core::__generate_unique(int num, int maxNum, int index, int result[]) {if(index>=81||maxNum<=num) return maxNum<=num;int x = result[index];result[index] = 0;if(isUniqueSolve(result)) {return __generate_unique(num+1,maxNum,index+1,result);}else {result[index] = x;return __generate_unique(num,maxNum,index+1,result);} }void Core::generate(int number,int lower,int upper,bool unique,int result[][81]) {if(number<1||number>10000) throw NumException();if(lower>upper||lower<0||upper>64) throw RangeException();if(unique) {generate(number,result); #pragma omp parallel forfor(int n=0;n<number;++n) {int num = lower;//rand()%(upper-lower+1)+lower;__generate_unique(0,num,0,result[n]);}} else {generate(number,result);for(int n=0;n<number;++n) {int num = rand()%(upper-lower+1)+lower;for(int i=0;i<81;++i) {if(81-i+1<=num) {result[n][i] = 0;--num;}else {double r = rand()/(double)RAND_MAX;if(r<0.5) {result[n][i] = 0;--num;}}}}} }在檢查函數(shù)上,Core類提供了三個接口以供檢查數(shù)獨合法性等信息:
// check functions bool check_valid(int *solution); int check_valid(int number,int *solution); bool check_same(int number,int *solution);其中check_valid為檢查輸入數(shù)獨是否為合法數(shù)獨,即每個數(shù)字都是0~9之間的整數(shù)且符合數(shù)獨的規(guī)則要求。如果是合法的bool check_valid(int)會返回true,int check_valid(int,int)會返回0,否則為非法數(shù)獨。其中bool類型的需要保證輸入的數(shù)獨是只有一個的,且所有的函數(shù)輸入都至少分配好足夠的內存空間。
我實現(xiàn)了其中的-m生成部分。
我的-m生成難度的是基于挖的空的數(shù)量和空間自由度兩個方面決定。
一般而言,肯定是挖空越多總體趨勢上難度越大。
難度等級的增加,空格數(shù)總體趨勢遞增,不同難度等級的題目空格數(shù)也一樣的情況。我們得出初步結論,簡單按照空格的數(shù)目多少來劃分數(shù)獨題目的難易程度是不全面的。同樣空格數(shù)的數(shù)獨題目,空格數(shù)分布位置的不同對難度等級也造成影響。
在這種思維下,我們提出自由度的概念:,數(shù)獨的難度等級與行、列、宮格內的空格數(shù)存在著聯(lián)系。提出以空格自由度衡量數(shù)獨的難度等級。數(shù)獨的空格自由度,指除掉空格本身,空格所在行、列、九宮內的空格數(shù)總和。
我們通過這兩個方面的思維,將空格從20到55分為3個區(qū)間,自由度從700到1300分為三個區(qū)間,難度的劃分是只要一個條件達到了相應區(qū)間就可以認為達到了要求。
這樣我們可以在第一次個人項目的基礎上,隨即進行挖空,直至滿足了條件。
可以參考
http://www.cnblogs.com/candyhuang/archive/2011/12/21/2153668.html
(5)閱讀有關UML的內容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。畫出UML圖顯示計算模塊部分各個實體之間的關系(畫一個圖即可)。(2’)
UML圖
計算模塊接口部分的性能改進
原來的回溯算法性能很低,所以在求解上,由個人項目時的回溯法改為了快速求解精確覆蓋問題的DLX算法。但是在實現(xiàn)后卻發(fā)現(xiàn)加速并不明顯,甚至還有些慢。在最耗時間的生成唯一解的情況下,用VS性能分析后得到如下圖:
[1]!(http://images2017.cnblogs.com/blog/1224149/201710/1224149-20171015145603137-289738865.png)
由于我截圖的時候代碼有些改動導致函數(shù)名并不能正常顯示。圖中最耗時間的是DLXSolver中的solve函數(shù),由于查找唯一解時挖空是遞歸搜索,所以這是很正常的現(xiàn)象。但是注意到第二位的那個new函數(shù),是分配空間的函數(shù),這個耗時也很長,這是為什么?看看代碼,發(fā)現(xiàn)在將數(shù)獨問題轉換為精準覆蓋問題時會申請內存空間來建立雙向十字鏈表來存儲稀疏矩陣。由于原來的實現(xiàn)為一個節(jié)點一個節(jié)點進行內存分配,所以只要改為剛開始統(tǒng)一分配好一批節(jié)點后,后面按需所配即可。改進后速度有了大大提高.
計算模塊部分單元測試展示
單元測試思路為每個主要的函數(shù)都進行測試。I/O操作的單元測試,其中refRes是一個合法的數(shù)獨數(shù)組:
TEST_METHOD(TestRead){Core core;int refRes[]={...};int* result;core.read_sudoku(&result,"sudokuexp.txt");for(int i=0;i<162;++i)Assert::AreEqual(refRes[i],result[i]);delete[] result;}TEST_METHOD(TestWrite){Core core;int refRes[]={...};int* result;core.write_sudoku(2,refRes,"sudoku.txt");core.read_sudoku(&result,"sudoku.txt");for(int i=0;i<162;++i)Assert::AreEqual(refRes[i],result[i]);delete[] result;}對于generate,對于每個不同大小的輸入、不同類型的接口進行測試,并在最后調用檢查函數(shù)check_valid和check_same來驗證正確性。
TEST_METHOD(TestGen...){Core core;const int N = ...; // MAXIMUM//int result[N][81];int* result = new int[N*81];core.generate(N,(int(*)[81])result);Assert::AreEqual(core.check_valid(N,(int*)result),0);Assert::IsTrue(core.check_same(N,(int*)result));delete[] result;}Solve函數(shù)同樣,讀入測試數(shù)據(jù)后調用solve接口進行解題,然后用check_valid進行正確性檢查。
TEST_METHOD(TestSolveExp){Core core;const int N = 2; // MAXIMUMint* result = new int[N*81];//int result[N][81];int* puzzle;core.read_sudoku(&puzzle,"sudokuexp.txt");core.solve(N,puzzle,(int*)result);Assert::AreEqual(core.check_valid(N,(int*)result),0);Assert::IsTrue(core.check_same(N,(int*)result));delete[] result;delete[] puzzle;}單元測試結果如下:
(7)看Design by Contract, Code Contract的內容:
http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述這些做法的優(yōu)缺點, 說明你是如何把它們融入結對作業(yè)中的。(5')
這兩種做法指的都是約定性地進行代碼工作,兩個人實現(xiàn)就兩者要進行對接的函數(shù)的條件,參數(shù)條件(包括參數(shù)的取值范圍,類型),各自明確在什么條件下,接受什么條件的指令,不滿足條件的可以不接受,這種方式的優(yōu)點在于能夠更好地敦促雙方按照實現(xiàn)的約定嚴格書寫代碼,提高的代碼的魯棒性和健壯性,有可能促成高水平工程的完成,但是缺點在于如果兩個人水平有一定差距的話,不利于兩個人更加方便地進行合作編程,會導致木桶效應的發(fā)生。
計算模塊部分異常處理說明
計算模塊異常有四類:RangeException,NumException,ModeException,InvalidSudokuException。每個異常都在函數(shù)剛開始進行參數(shù)檢查時,如果參數(shù)異常則拋出相關異常。單元測試如下,以下面為例:
TEST_METHOD(TestInvalidSudokuException){Core core;const int N = 1; // MAXIMUMint puzzle[] ={9, 9, 8, 0, 6, 0, 1, 2, 4,2, 3, 7, 4, 5, 1, 9, 6, 8,1, 4, 6, 0, 2, 0, 3, 5, 7,0, 1, 2, 0, 7, 0, 5, 9, 3,0, 7, 3, 0, 1, 0, 4, 8, 2,4, 8, 0, 0, 0, 5, 6, 0, 1,7, 0, 4, 5, 9, 0, 8, 1, 6,8, 1, 0, 7, 4, 6, 2, 0, 0,3, 0, 5, 0, 8, 0, 7, 0, 9,};int result[81]={ 0 };try {Assert::IsFalse(core.check_valid(puzzle));Assert::AreEqual(core.check_valid(1,puzzle),1);core.solve(puzzle,(int*)result);Assert::Fail();}catch(InvalidSudokuException e) {}}(10)界面模塊的詳細設計過程。在博客中詳細介紹界面模塊是如何設計的,并寫一些必要的代碼說明解釋實現(xiàn)過程。(5')
首先是一個計時的工具,通過Qlabel來實現(xiàn),在時間能夠走得情況下(通過全局變量changable來控制),將time_show增長,并且將其的分鐘數(shù)和秒數(shù)set到兩個label的txt上面,就是先了記時的功能。
接下來是各個控制按鈕,它們的類型都是QPushButton,它們之間有一定的邏輯關系,比如在剛剛按下new_game按鈕的時候,除了restart和new_game之外的按鈕都是不能按下的,而一旦選擇了數(shù)獨中的按鈕,那么根據(jù)選擇的是原先就有的,新填上的還是現(xiàn)在為止還沒有填上的,不同的按鈕會分別轉換為可選中和不可選中的狀態(tài),以下面的代碼為例
最后的成品大概是這樣的,可以計時、選擇難度、開新局,重新此局,刪除填過的空。
(11)界面模塊與計算模塊的對接。詳細地描述UI模塊的設計與兩個模塊的對接,并在博客中截圖實現(xiàn)的功能。(4')
通過與難度生成(-m),求解的函數(shù)對接來實現(xiàn)UI模塊求解的全過程。
void Core::generate(int number,int mode,int result[][81]) {if(number<1||number>10000) throw NumException();if(mode!=1&&mode!=2&&mode!=3) throw ModeException();int i = 0;int free_degree = 0;int sudoku_num = 0;int num = 0;int the_time = 0;int hollow_num = 0;for (sudoku_num = 0; sudoku_num < number; sudoku_num++){generate(1,(int(*)[81])result[sudoku_num]);free_degree = 0;num = 0;if (mode == 1){hollow_num=rand() % 9 + 40;if (the_time == 0){for (int ii = 0;; ii = ii + 2){上面這部分是實現(xiàn)難度生成的代碼,而這部分的代碼通過什么樣的方式與UI的工程相聯(lián)系起來呢,主要是通過動態(tài)鏈接庫所生成的代碼庫,UI通過調用來實現(xiàn)的。在UI的顯示類中將core定義為其中的一個屬性,然后通過調用生成和求解的方法將相應的結果返回到UI模塊的一個變量result中,再通過UI的settext將其顯示在圖形界面上。
具體代碼思想大概是這樣:
(12)描述結對的過程,提供非擺拍的兩人在討論的結對照片。(1')
(13)看教科書和其它參考書,網(wǎng)站中關于結對編程的章節(jié),例如:
http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
說明結對編程的優(yōu)點和缺點。
結對的每一個人的優(yōu)點和缺點在哪里 (要列出至少三個優(yōu)點和一個缺點)。(5')
結對編程的優(yōu)點在于兩個人之間可以互相合作著編程,可以避免很多平時個人編程中所出現(xiàn)的錯誤,但是因為結對編程必然要以一個人的代碼為主要模板,這就導致另一個人在參與的過程中會有一些生疏和不習慣,比較多的時間都用來理解對方的代碼,其次,則是合作編程在時間上有時候不太好掌握,有時候會導致進度不統(tǒng)一,一個人早做完而另一個一直在做的情況也可能出現(xiàn)。
同伴優(yōu)點:代碼能力出眾,軟工大佬;代碼知識較為廣泛,能夠解決很多以前沒有接觸過方面的問題,大大加快了最后將前端和后端融合的進度;對于測試方面有足夠的知識,對于程序的正確性方面做出了很大貢獻。
缺點:缺乏足夠合理的溝通導致進度不夠統(tǒng)一,影響了效率。
轉載于:https://www.cnblogs.com/zhj233/p/7671035.html
總結
以上是生活随笔為你收集整理的[软件工程基础]结对项目 数独程序扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “Hello World!”团队第二次会
- 下一篇: 部署时服务端Excel的COM设置