https://github.com/KarCute/Wordlistgit
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 30 |
· Estimate | · 估计这个任务须要多少时间 | 10 | 30 |
Development | 开发 | 2565 | 2320 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 100 |
· Design Spec | · 生成设计文档 | 50 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 15 | 30 |
· Design | · 具体设计 | 90 | 120 |
· Coding | · 具体编码 | 2000 | 1680 |
· Code Review | · 代码复审 | 60 | 40 |
· Test | · 测试(自我测试,修改代码,提交修改) | 200 | 300 |
Reporting | 报告 | 370 | 510 |
· Test Report | · 测试报告 | 120 | 200 |
· Size Measurement | · 计算工做量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 过后总结, 并提出过程改进计划 | 240 | 300 |
合计 | 2945 | 2860 |
In computer science, information hiding is the principle of segregation of the design decisions in a computer program that are most likely to change, thus protecting other parts of the program from extensive modification if the design decision is changed. The protection involves providing a stable interface which protects the remainder of the program from the implementation (the details that are most likely to change).github
维基百科——Information Hiding
在这里信息隐藏并非指信息加密或者隐匿,而是一种设计思想,将程序中设计决策中最容易改变的部分分离开来,从而保护其余部分不受影响。在这里咱们的设计自始至终都将计算接口剥离开单独处理,由于这部分自己较难实现,而且在优化代码算法时也是只须要对这部分进行改进。算法
Interface除了接口外还有界面的意思,通常说来User Interface Design指的是UI设计。这里应该指的接口设计。
咱们在设计时是按照指定的接口完成了计算模块的设计,同时注意到,虽然咱们在整个项目实现时,知足了传入接口的参数都保证正确,可是对用户来讲,这个接口内部是未知的,他们不必定会按照规范传入参数,所以须要考虑到设计的完备性,即异常处理。编程
In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components.数组
耦合是软件结构中各模块之间相互链接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及经过接口的数据。app
百度百科——高内聚低耦合
早在接触面向对象时就已经知道了好的程序应该作到“高内聚,低耦合”。在咱们的编程中,模块的接口设计大多传入参数并不复杂,耦合程度较低。如读取文件部分,咱们只接受一个文件名,而后就能将单词存入一个vector容器中。其他模块只须要接受这个容器,而不须要与该模块有较多的交互。数据结构和算法
之前作过相似的题,输入的全部单词可否所有首尾相连造成链。因为单词首尾相连有多种链接方式,故基本的数据结构为图。
建图有两种方式,一种是以单词为节点,若是单词间正好能够首尾链接,则添加一条边,该边即为链接的字母。另外一种建图方式是以字母为节点,以单词为边,出现一个单词,即把首字母节点向尾字母节点添加一条边,边的值即为该单词。
对于这道题目而言,因为单词须要输出,加之对第二种建图方式掌握并不熟练,所以选择的是第一种建图方式。
模型确立后,问题就能够简化成“求图中的最长链”,即最长路径问题,显然问题是多源最长路径问题。函数
数据结构为图,存储方式为邻接矩阵,理由是能更契合floyd算法。
对于无环状况,因为为多源最长路径问题,联想到最短路径问题,能够肯定为floyd算法。
而对于有环状况,因为出现了正值环,floyd算法再也不适用。在找不到更有解决方法的状况下,只能适用DFS深度优先搜索求解。oop
ReadFile: 读取文件的模块,将文件中的单词提取进入容器vector中。
Graph: 图的定义。
InputHandler:处理输入的模块,读取命令行并处理参数。
FindLongestWordList: 计算模块,内含计算接口。计算出单词中的最长链。
首先须要判断有无环,对于没有-r参数的输入来讲,若是有环须要报错。这里也是用到DFS的染色算法。每一个点有三种状态:未遍历过,遍历过,当前序列正在遍历。若是一次DFS中一个点与正在遍历中的点相连了,说明DFS回到了以前的点,即图中有环。
另外一问题是因为无环状况最多可有10000个单词,而floyd算法时间复杂度为O(\(n^3\)),暴力的计算显然是不行的。考虑到对于无环的状况,有以下特性:对于单词element和elephant,因为无环,这两个单词最多只有一个会出如今链中。(不然会出现element, t..., ..., ....e, elephant / element,这样必定是有环的),而若是要知足字母最多,显然这时候须要选择elephant加入链中。所以咱们能够对于全部首尾字母相同的单词,保留首尾字母组合中,最长的一个单词。这样的操做以后,最多的单词数目为351,即便是时间复杂度O(\(n^3\))的算法也能很快得出结果。另外能够计算得,最长链的长度最大为51。
首先是无环状况,其性能最大阻碍是10000个单词大样本状况下,floyd算法时间复杂度太高致使的。可是在4.4有介绍过,咱们能够经过无环单词链的特性来削减样本数量,削减后单词数量少,即便时间复杂度高也能很快跑出结果。所以性能方面上没有太大问题。
其次是有环状况,因为DFS算法仍属于暴力递归搜索,并不算很好的算法,其性能也着实较差。可是咱们也想不到更好的解决算法,因此并无改进。
契约式设计:定义正式、精确和可验证的接口规范。
在单元测试部分咱们对程序中除输出部分外(因为输出部分只是一个简单的输出到文件)其余因此部分或函数进行的全面的单元测试,如图共25个。
TEST_METHOD(TestMethod3) { // TODO: normal_test3 char* words[101] = { "element", "heaven", "table", "teach", "talk"}; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } int l = gen_chain_word(words, 5, answer, 0, 0, true); Assert::AreEqual(l, 4); Assert::AreEqual("table", answer[0]); Assert::AreEqual("element", answer[1]); Assert::AreEqual("teach", answer[2]); Assert::AreEqual("heaven", answer[3]); for (int i = 0; i < 101; i++) { free(answer[i]); } }
TEST_METHOD(TestMethod6) { // TODO: normal_test6 char* words[101] = { "apple", "banane", "cane", "a", "papa", "erase" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } int l = gen_chain_char(words, 6, answer, 'a', 'e', false); Assert::AreEqual(l, 3); Assert::AreEqual("a", answer[0]); Assert::AreEqual("apple", answer[1]); Assert::AreEqual("erase", answer[2]); for (int i = 0; i < 101; i++) { free(answer[i]); } }
TEST_METHOD(TestMethod2) { // 正确_2 int argc = 6; char* argv[101] = { "Wordlist.exe", "-r", "-h", "a", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::AreEqual(enable_loop, true); Assert::AreEqual(word_or_char, 2); Assert::AreEqual(head, 'a'); Assert::AreEqual(tail, char(0)); Assert::AreEqual(Filename, (string)"test_1.txt"); }
整体测试思路为控制变量,即控制是否有首字母、尾字母约束,是否带环,是最多单词仍是最多字符。
单元测试覆盖率截图(因为C++没有找到直接测试单元测试覆盖率的插件,这里用的方法是将单元测试代码移至main函数中用OpenCppCoverage插件获得的覆盖率,部分异常测试没有放进来,因此覆盖率没有达到100%)
TEST_METHOD(TestMethod3) { // 错误_1 int argc = 5; char* argv[101] = { "Wordlist.exe", "-r", "-r", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception1& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是‘-r’出现了两次,错误的参数组合。
TEST_METHOD(TestMethod7) { // 错误_5 int argc = 6; char* argv[101] = { "Wordlist.exe", "-r", "-h", "1", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception2& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是‘-h’指定首字母为‘1’,明显是错误的。
TEST_METHOD(TestMethod9) { // 错误_7 int argc = 5; char* argv[101] = { "Wordlist.exe", "-b", "-r", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception3& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是输入参数‘-b’显然是不符合规定的。
TEST_METHOD(TestMethod2) { // 错误 vector <string> words; try { ReadFile("normal_test3.txt", words); // 不存在的文件 Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是测试了一个在此路径下不存在的文件。
TEST_METHOD(TestMethod2) { // 错误 vector <string> words; try { ReadFile("long_word_test.txt", words); Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是文件中存在长度超过600的单词。
TEST_METHOD(TestMethod2) { // 错误 vector <string> words; try { ReadFile("more_words_test.txt", words); Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是所测试文件中单词数超过了10000。
TEST_METHOD(TestMethod10) { // wrong_test2 char* words[101] = { "alement", "oeaven", "tabla", "teaco", "talk" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } try { int l = gen_chain_char(words, 5, answer, 0, 'n', false); Assert::IsTrue(false); } catch (myexception7& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是传入单词能够造成环,且用户没有传入参数‘-r’。
TEST_METHOD(TestMethod11) { // wrong_test3 char* words[101] = { "alement", "oeaven", "tabla", "teaco", "talk" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } try { int l = gen_chain_word(words, 5, answer, 'b', 'n', true); Assert::IsTrue(false); } catch (myexception8& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
这个单元测试是规定了首尾字母后,单词链中没有用户所要求的单词链。
因为与队友为舍友,结对时相对简单不少,只须要到对铺和队友一块儿结对编程就好了。咱们的水平差很少, 编程能力和数据结构算法的掌握都不算太好。初期时咱们主要是一块儿讨论算法,如何实现基本的功能,数据结构应该用什么。敲定一个算法以后就开始分头找资料,最后再汇总资料,交给他来敲代码或者我来在一些地方进行修改。编写时常常会遇到一些意料不到的bug,最后必须一块儿搜索如何解决。可是两我的在一块儿编写代码时,有一我的来随时审视代码,有不懂的地方或者不对劲的地方另外一人均可以随时提出来。所以虽然结对编程效率没有提升, 可是效果会比两个单人编写来的更好。
总的来讲此次题目难度仍是没有那么爆炸,因此咱们之间的合做也比较愉快。至于提意见的艺术是根本用不上的,毕竟是舍友也不会产生矛盾。下面是咱们在初期时讨论算法的图片:
本身的话,优势是设计时肯思考,能接受对方的意见,愿意花时间寻找资料。缺点多是有点爱开小差。 对对方的话,优势是能主动找资料,能包容个人不足,在我有事时能主动在差很少的测试上花时间编写。缺点多是和我同样数据结构和算法掌握不够好。