天天看點

結對項目-數獨程式擴充

1.項目位址

計算子產品 : https://github.com/easylliu/gitlearning/tree/master/Sudoku

GUI: https://github.com/qwellk/project1/tree/product2/WindowsFormsApp1

2.開發時間

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30 40
Estimate 估計這個任務需要多少時間 15
Development 開發
Analysis 需求分析 (包括學習新技術) 60
Design Spec 生成設計文檔
Design Review 設計複審 (和同僚稽核設計文檔)
Coding Standard 代碼規範 (為目前的開發制定合适的規範)
Design 具體設計 80
Coding 具體編碼 240 480
Code Review 代碼複審 120
Test 測試(自我測試,修改代碼,送出修改) 180
Reporting 報告
Test Report 測試報告
Size Measurement 計算工作量 10 20
Postmortem & Process Improvement Plan 事後總結, 并提出過程改進計劃
合計 715 1145

3.看教科書和其它資料中關于Information Hiding, Interface Design, Loose Coupling的章節,說明你們在結對程式設計中是如何利用這些方法對接口進行設計的。

information Hiding :資訊隐藏是一種重要的軟體開發手段,它與對象的封裝與子產品化密切相關。在這次結對程式設計中,我們把計算核心和輸入輸出處理全部封裝在core子產品中,通過接口将core子產品和外界聯系起來,但具體的實作和錯誤處理都被隐藏在子產品中。

Interface Design:由于和結對隊友所用語言不同,我們之間的對接至關重要,通過數獨計算和求解功能設計接口,然後通過gui功能增加簡化接口,使得計算子產品和ui子產品的對接更加容易;然後通過前面的規範化程式設計使得我們對程式設計的格式規範也有了統一意見,以此設計接口;

Loose Coupling:改變或者供應者或者服務的問題不能影響到使用者----或者使用者的問題不應影響到供應者或者服務。相對來說,這次的計算子產品和ui子產品松耦合就不錯,通過子產品的封裝減少通過接口連接配接的各個子產品的影響,例如對core子產品和ui子產品的對接,當計算核心出現問題時,在core子產品内部錯誤處理,減少對ui的影響,以此實作接口設計。

4計算子產品接口的設計與實作過程。設計包括代碼如何組織,比如會有幾個類,幾個函數,他們之間關系如何,關鍵函數是否需要畫出流程圖?說明你的算法的關鍵(不必列出源代碼),以及獨到之處。

首先,通過思考分析計算子產品所包括的範圍,有些是計算資料的(生成數獨,求解數獨),有些是控制輸入的(檔案讀入,控制台讀入),有些是資料可視化的(檔案輸出),是以主要子產品接口就是實作數獨的生成,求解,檔案讀出,檔案寫入和控制台讀入等等幾個大塊。然後相對來說生成數獨和求解數獨又可以分成幾個小塊,檔案讀出,檔案寫入和控制台讀入等等可以分别用一個函數實作;就生成來說,數獨難易度的劃分(-m),挖空數量的多少(-r),是否唯一(-u)等等就可以具體到函數;如此分析下來,計算子產品接口的大體架構就已經出來了。我感覺我算法的關鍵就是實作數獨生成函數這個大塊,相對來說就是實作那三個接口:生成一定數量數獨終盤,生成一定數量區分難易度的數獨,生成挖去一定數量空的數獨;将三個函數實作,剩下的就是小塊了。獨到之處并沒有感覺特别有特别之處,正常實作吧。在數獨的難易度劃分上,我根據挖空數來區分,因為相對來說在随機分布的情況下挖空數量多的相對更加困難。然後簡單的挖空數範圍[11,29),中等[29,47),困難[47,65);對于每種難度的數獨在範圍内随機生成要挖空數量,然後随機挖去那麼多數量的空,由于初始17個空就已确定唯一解,是以最終還會是唯一解。

5.讀有關UML的内容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。畫出UML圖顯示計算子產品部分各個實體之間的關系(畫一個圖即可)。

相對來說,因為我是所有的計算函數實作全部用一個core類子產品搞定,是以各實體關系也在core子產品内部表現,關系基本表現在計算與輸入輸出的聯系。

結對項目-數獨程式擴充

6.計算子產品接口部分的性能改進。記錄在改進計算子產品性能上所花費的時間,描述你改進的思路,并展示一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),并展示你程式中消耗最大的函數.

在計算子產品上,因為生成數獨的方法我們是利用已有數獨生成的,是以主要性能改進花在了求解數獨上,由開始的純暴力周遊回溯在面對17提示數獨求解上花費時間過久,随後改進通過每次找最少可選集回溯比原來純暴力解決提高了不少,但由于沒有精準覆寫的問題導緻解一些困難數獨花費時間很久,仍有待提高。如圖時求解200多個17提示數獨性能分析,由于性能上面沒有知道具體函數,通過函數實作可知消耗最大的是回溯函數deal().

結對項目-數獨程式擴充

7.看Design by Contract, Code Contract的内容,描述這些做法的優缺點, 說明你是如何把它們融入結對作業中的。

契約式設計就是按照某種規定對一些資料等做出約定,如果超出約定,程式将不再運作,例如要求輸入的參數必須滿足某種條件;優點是保證了調用者和被調用者雙方的品質,缺點就是在生産中無法自由地把這些契約disable;

在我們的結對作業中,對于子產品間使用了契約的思想,一人負責core子產品,一份負責gui,保證雙方地位的平等。調用者的傳入參數必須是正确的,否則責任不在被調用者,而在傳入者。

8.計算子產品部分單元測試展示。展示出項目部分單元測試代碼,并說明測試的函數,構造測試資料的思路。并将單元測試得到的測試覆寫率截圖,發表在部落格中。要求總體覆寫率到90%以上,否則單元測試部分視作無效。

generate()

測試三個generate接口,分别對應-c,-m,-r;

通過調用函數生成數獨,對最終生成的數獨進行有效性判斷和重複性判斷。

TEST_METHOD(generate1)
{
			// TODO: 在此輸入測試代碼
			core core0;
			int result[100][81];
			int sudo[9][9];
			core0.generate(100, result);
			for (int i = 0; i < 100; i++)
			{
				memcpy(sudo, result[i], sizeof(sudo));
				Assert::AreEqual(core0.isvalid(sudo), true);
			}
			Assert::AreEqual(core0.isunique(100, result), true);
}
TEST_METHOD(generate2)
{
			// TODO: 在此輸入測試代碼
			core core0;
			int result[100][81];
			int sudo[9][9];
			for (int i = 1; i <= 3; i++)
			{
				core0.generate(100, i, result);
				for (int k = 0; k < 100; k++)
				{
					memcpy(sudo, result[k], sizeof(sudo));
					Assert::AreEqual(core0.isvalid(sudo), true);
					int count = 0;
					for (int j = 0; j < 81; j++)
					{
						if (result[k][j] == 0) count++;
					}
					if (i == 1)
					{
						Assert::AreEqual(count >= 11 && count<29, true);
					}
					if (i == 2)
					{
						Assert::AreEqual(count >= 29 && count <47, true);
					}
					if (i == 3)
					{
						Assert::AreEqual(count >= 47 && count <65, true);
					}
				}
			}
}
TEST_METHOD(generate3)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			int result[100][81];
			int sudo[9][9];
			int lower = 22, upper = 45;
			core0.generate(100, lower, upper, false, result);
			for (int i = 0; i < 100; i++)
			{
				memcpy(sudo, result[i], sizeof(sudo));
				Assert::AreEqual(core0.isvalid(sudo), true);
				int count = 0;
				for (int j = 0; j < 81; j++)
				{
					if (result[i][j] == 0) count++;
				}
				Assert::AreEqual(count >= lower&&count <= upper, true);
			}
			core0.generate(100, lower, upper, true, result);
			for (int i = 0; i < 100; i++)
			{
				memcpy(sudo, result[i], sizeof(sudo));
				Assert::AreEqual(core0.isvalid(sudo), true);
				int count = 0;
				for (int j = 0; j < 81; j++)
				{
					if (result[i][j] == 0) count++;
				}
				Assert::AreEqual(count >= lower&&count <= upper, true);
			}
		}
           

下圖測試solve函數,通過數獨求解根據傳回值判斷是否有解

TEST_METHOD(solve)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			int result[1][81];
			int sudo[81];
			int lower = 22, upper = 45;
			core0.generate(1, 1, result);
			Assert::AreEqual(core0.solve(result[0], sudo), true);
			core0.generate(1, 2, result);
			Assert::AreEqual(core0.solve(result[0], sudo), true);
			core0.generate(1, 3, result);
			Assert::AreEqual(core0.solve(result[0], sudo), true);
			core0.generate(1, lower, upper, false, result);
			Assert::AreEqual(core0.solve(result[0], sudo), true);
		}
           

判斷數獨有效性函數,通過傳入一個有效數獨調用看傳回值是否正确

TEST_METHOD(isvalid)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			int sudo[9][9] = { 9,5,8,3,6,7,1,2,4,
				2,3,7,4,5,1,9,6,8,
				1,4,6,9,2,8,3,5,7,
				6,1,2,8,7,4,5,9,3,
				5,7,3,6,1,9,4,8,2,
				4,8,9,2,3,5,6,7,1,
				7,2,4,5,9,3,8,1,6,
				8,9,1,7,4,6,2,3,5,
				3,6,5,1,8,2,7,4,9
			};
			bool s = core0.isvalid(sudo);
			Assert::IsTrue(s);
		}
           

讀檔案測試,通過從檔案讀取數獨和實際數獨判斷是否一緻來測試讀檔案函數是否正确

TEST_METHOD(input_sudo)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			int sudo[9][9] = { 9,5,8,3,6,7,1,2,4,
				2,3,7,4,5,1,9,6,8,
				1,4,6,9,2,8,3,5,7,
				6,1,2,8,7,4,5,9,3,
				5,7,3,6,1,9,4,8,2,
				4,8,9,2,3,5,6,7,1,
				7,2,4,5,9,3,8,1,6,
				8,9,1,7,4,6,2,3,5,
				3,6,5,1,8,2,7,4,9
			};
			int result[1][81];
			int result1[1][81];
			memcpy(result1[0], sudo, sizeof(result1[0]));
			core0.input_sudo(result, "su.txt");
			for (int i = 0; i<81; i++)
				Assert::AreEqual(result[0][i], result1[0][i]);
		}
           

寫檔案測試,通過往檔案寫入數獨在通過已測試過的讀檔案讀出數獨判斷是否一緻來測試寫入檔案

TEST_METHOD(print_sudo)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			int sudo[9][9] = { 9,5,8,3,6,7,1,2,4,
				2,3,7,4,5,1,9,6,8,
				1,4,6,9,2,8,3,5,7,
				6,1,2,8,7,4,5,9,3,
				5,7,3,6,1,9,4,8,2,
				4,8,9,2,3,5,6,7,1,
				7,2,4,5,9,3,8,1,6,
				8,9,1,7,4,6,2,3,5,
				3,6,5,1,8,2,7,4,9
			};
			int result[1][81];
			int result1[1][81];
			memcpy(result[0], sudo, sizeof(result[0]));
			core0.print_sudo(1, result, "su1.txt");
			core0.input_sudo(result1, "su1.txt");
			for (int i = 0; i < 81; i++) {
				Assert::AreEqual(result[0][i], result1[0][i]);
			}
		}
           
結對項目-數獨程式擴充
結對項目-數獨程式擴充

9.計算子產品部分異常處理說明。在部落格中詳細介紹每種異常的設計目标。每種異常都要選擇一個單元測試樣例釋出在部落格中,并指明錯誤對應的場景。

對各種輸入錯誤的測試,定義6種類型的輸入錯誤,每種分别測試一個看傳回錯誤是否一緻

包括,-c num錯誤,-s 檔案錯誤,-n num錯誤,-m mode錯誤,-r num錯誤和-r -m同時出現錯誤

TEST_METHOD(input_deal)
		{
			// TODO: 在此輸入測試代碼
			core core0;
			char *s_c0[2] = { "-c","dgh" };//-1  -C num 
			char *s_c1[2] = { "-c","1000001" };//-1
			char *s_s[2] = { "-s","fhdfjh" };//-2  -S file
			char *s_n[4] = { "-n","10001","-m","1" };//-3 -n num
			char *s_m[4] = { "-n","1","-m","4" };//-4 -m num
			char *s_r[4] = { "-n","1","-r","20~56" };//-5 -r num
			char *s_rm[6] = { "-n","1","-r","20~56","-m","1" }; //-6 -m -r
			Assert::AreEqual(core0.input_deal(2, s_c0), -1);
			Assert::AreEqual(core0.input_deal(2, s_c1), -1);
			Assert::AreEqual(core0.input_deal(2, s_s), -2);
			Assert::AreEqual(core0.input_deal(4, s_n), -3);
			Assert::AreEqual(core0.input_deal(4, s_m), -4);
			//	Assert::AreEqual(core0.input_deal(4, s_r), -5);
			//	Assert::AreEqual(core0.input_deal(6, s_rm), -6);
		}
           

10.界面子產品的詳細設計過程。在部落格中詳細介紹界面子產品是如何設計的,并寫一些必要的代碼說明解釋實作過程。

首先生成9*9的格子作為數獨界面,然後再生成開始和提示button,生成始終和難度label,生成難度和排行榜菜單;

然後給button和格子響應事件,實作相應的功能,通過菜單調節難度。

結對項目-數獨程式擴充
結對項目-數獨程式擴充

11.界面子產品與計算子產品的對接。詳細地描述UI子產品的設計與兩個子產品的對接,并在部落格中截圖實作的功能。

通過計算子產品的數獨生成功能生成界面子產品顯示的難度對應的數獨,然後通過ui子產品将生成的數獨在界面子產品生成數獨,

界面子產品的難度選擇通過ui子產品試計算子產品生成相應的數獨;界面子產品的提示功能通過ui子產品找到對應的數獨答案做出提示等等。

ui子產品作為中間子產品連接配接計算子產品和界面子產品。

結對項目-數獨程式擴充

12.描述結對的過程,提供非擺拍的兩人在讨論的結對照片。

結對項目-數獨程式擴充

13.說明結對程式設計的優點和缺點。結對的每一個人的優點和缺點在哪裡 (要列出至少三個優點和一個缺點)。

結對程式設計優點:

(1)在開發層次,結對程式設計能提供更好的設計品質和代碼品質,兩人合作能有更強的解決問題的能力。

(2)對開發人員自身來說,結對工作能帶來更多的信心,高品質的産出能帶來更高的滿足感。

(3)在心理上, 當有另一個人在你身邊和你緊密配合, 做同樣一件事情的時候, 你不好意思開小差, 也不好意思糊弄。

(4)在企業管理層次上,結對能更有效地交流,互相學習和傳遞經驗,能更好地處理人員流動。因為一個人的知識已經被其他人共享。

缺點:

1.對于有不同習慣的程式設計人員,可以在起工作會産生麻煩,甚至沖突。

2.有時候,程式員們會對一個問題各執己見(代碼風格可能會是引發技術人員口水戰的地方),争吵不休,反而産生重大内耗。

3.兩個人在一起工作可能會出現工作精力不能集中的情況。程式員可能會交談一些與工作無關的事情,反而分散注意力,導緻效率比單人更為低下。

4.結對程式設計可能讓程式員們互相學習得更快。有些時候,學習對方的長外,可能會和程式員們在起滋生不良氣氛一樣快。比如,合夥應付工作,敷衍項目。

5.面對新手,有經驗的老手可能會覺得非常的煩躁。不合适的溝通會導到團隊的不和諧。

6.新手在面對有經驗的老手時會顯得非常的緊張和不安,甚至出現害怕焦慮的的精神狀态,進而總是出現低級錯誤,而老手站在他們後面不停地指責他們導緻他們更加緊張,出現惡性循環。最終導緻項目進展效率低下,并且團隊貌合神離。

7.有經驗的人更喜歡單兵作戰,找個人來站在他背後看着他可能會讓他感到非常的不爽,最終導緻程式設計時受到情緒影響,反而出現反作用。

優點:沉着,穩重,思維較好

缺點:有拖延症