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