结对项目-数独程序扩展
github链接:
15061085石奇川:https://github.com/qwellk/project1/tree/product2/WindowsFormsApp1
15061080刘亚洲: https://github.com/qwellk/project1/tree/product2/WindowsFormsApp1
psp时间估计:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 40 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 60 | 30 |
· Design Spec | · 生成设计文档 | ||
· Design Review | · 设计复审 (和同事审核设计文档) | ||
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 60 | 80 |
· Coding | · 具体编码 | 240 | 480 |
· Code Review | · 代码复审 | 60 | 120 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 180 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | 120 |
· Size Measurement | · 计算工作量 | 10 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 715 |
看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的。
information Hiding :信息隐藏是一种重要的软件开发手段,它与对象的封装与模块化密切相关。在这次结对编程中,我们把计算核心和输入输出处理全部封装在core模块中,通过接口将core模块和外界联系起来,但具体的实现和错误处理都被隐藏在模块中。 Interface Design:由于和结对队友所用语言不同,我们之间的对接至关重要,通过数独计算和求解功能设计接口,然后通过gui功能增加简化接口,使得计算模块和ui模块的对接更加容易;然后通过前面的规范化编程使得我们对编程的格式规范也有了统一意见,以此设计接口; Loose Coupling:改变或者供应者或者服务的问题不能影响到用户----或者用户的问题不应影响到供应者或者服务。相对来说,这次的计算模块和ui模块松耦合就不错,通过模块的封装减少通过接口连接的各个模块的影响,例如对core模块和ui模块的对接,当计算核心出现问题时,在core模块内部错误处理,减少对ui的影响,以此实现接口设计。
计算模块接口的设计与实现过程:
设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。首先,通过思考分析计算模块所包括的范围,有些是计算数据的(生成数独,求解数独),有些是控制输入的(文件读入,控制台读入),有些是数据可视化的(文件输出),因此主要模块接口就是实现数独的生成,求解,文件读出,文件写入和控制台读入等等几个大块。然后相对来说生成数独和求解数独又可以分成几个小块,文件读出,文件写入和控制台读入等等可以分别用一个函数实现;就生成来说,数独难易度的划分(-m),挖空数量的多少(-r),是否唯一(-u)等等就可以具体到函数;如此分析下来,计算模块接口的大体框架就已经出来了。我感觉我算法的关键就是实现数独生成函数这个大块,相对来说就是实现那三个接口:生成一定数量数独终盘,生成一定数量区分难易度的数独,生成挖去一定数量空的数独;将三个函数实现,剩下的就是小块了。独到之处并没有感觉特别有特别之处,正常实现吧。在数独的难易度划分上,我根据挖空数来区分,因为相对来说在随机分布的情况下挖空数量多的相对更加困难。然后简单的挖空数范围[11,29),中等[29,47),困难[47,65);对于每种难度的数独在范围内随机生成要挖空数量,然后随机挖去那么多数量的空,由于初始17个空就已确定唯一解,因此最终还会是唯一解。
读有关UML的内容:
https://en.wikipedia.org/wiki/Unified_Modeling_Language。画出UML图显示计算模块部分各个实体之间的关系(画一个图即可)。
相对来说,因为我是所有的计算函数实现全部用一个core类模块搞定,因此各实体关系也在core模块内部表现,关系基本表现在计算与输入输出的联系。
计算模块接口部分的性能改进:
在计算模块上,因为生成数独的方法我们是利用已有数独生成的,因此主要性能改进花在了求解数独上,由开始的纯暴力遍历回溯在面对17提示数独求解上花费时间过久,随后改进通过每次找最少可选集回溯比原来纯暴力解决提高了不少,但由于没有精准覆盖的问题导致解一些困难数独花费时间很久,仍有待提高。如图时求解200多个17提示数独性能分析,由于性能上面没有知道具体函数,通过函数实现可知消耗最大的是回溯函数deal().
看Design by Contract, Code Contract的内容,描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的。
契约式设计就是按照某种规定对一些数据等做出约定,如果超出约定,程序将不再运行,例如要求输入的参数必须满足某种条件;优点是保证了调用者和被调用者双方的质量,缺点就是在生产中无法自由地把这些契约disable; 在我们的结对作业中,对于模块间使用了契约的思想,一人负责core模块,一份负责gui,保证双方地位的平等。调用者的传入参数必须是正确的,否则责任不在被调用者,而在传入者。
计算模块部分单元测试展示。
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); } } ```
计算模块部分异常处理说明
对各种输入错误的测试,定义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); }
UI模块的设计:
分别实现4个功能:计时功能、难度选择功能、提示功能、数独游戏功能
计时功能:用单独的label和timer实现,美国一段时间改变label的值。
难度选择功能:设定全局变量:难度,当选择难度时,对label进行操作并对这个全局变量进行修改,当以后点击开始时,就可以调用特定的函数进行根据不同难度生成数独了。
提示功能:能够根据生成的矩阵找到答案矩阵,并将鼠标上次停留的位置上的数独答案显示出来。
数独游戏功能:初始化时会根据选择难度的不同生成数独,并用蓝色标记这些格子,且不能修改,在某个格子填过数后,格子颜色变为绿色,选定格子时,格子颜色变为黄色,并每次填数后对填的数进行判断操作,并判断熟读是否已经完全填对。
UI的接口实现
只调用了两个接口,分别对应生成数独和求解数独,生成两个具体的generate和solve函数。
团队合作照片
说明结对编程的优点和缺点。结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
结对编程的优点:
能够相互督促,加快进度,减少一方的缺点。
处理困难问题的速度变快。
增强相互的责任心,能够对项目的质量有保证。
结对编程的缺点:
有的时候会造成一方等一方的情况,大大减速进度。
有点不适应,或者说造成写代码的一方分心。
优点:有责任心,对时间的把握的好,写代码快。
缺点:没有