【软件工程】 结对做业

项目 内容
这个做业属于哪一个课程 软件工程 罗杰
这个做业的要求在哪里 结对项目 最长单词链
我在这个课程的目标是 熟悉软件开发总体流程,提高自身能力
这个做业在哪一个具体方面帮助我实现目标 实践教材中内容,体会“结对编程”模式



开发前的PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30
· Estimate · 估计这个任务须要多少时间 30
Development 开发 720
· Analysis · 需求分析 (包括学习新技术) 90
· Design Spec · 生成设计文档 60
· Design Review · 设计复审 (和同事审核设计文档) 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30
· Design · 具体设计 120
· Coding · 具体编码 210
· Code Review · 代码复审 60
· Test · 测试(自我测试,修改代码,提交修改) 120
Reporting 报告 90
· Test Report · 测试报告 30
· Size Measurement · 计算工做量 30
· Postmortem & Process Improvement Plan · 过后总结, 并提出过程改进计划 30
合计 840


  • Wikipedia中有关Information Hiding、Interface Design和Loose Coupling定义以下:
  • Information Hiding

     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).html

  • Interface Design

     (No relevant description.)git

  • Loose Coupling

     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. Subareas include the coupling of classes, interfaces, data, and services.github

其实这三者的共同目的或诉求就是:经过对接口进行设计,来保证类与类之间的隐私性、独立性,使属性不会过分共享(暴露),使对象的信息不可直接访问,而是要调用方法,从而保证对象的安全性。咱们在进行接口设计时,将每一个类的信息设为private属性,并提供获取属性和合理的修改属性的方法,而不能够直接访问或修改属性,并提供总的接口,可能内部要调用其余类的方法,但不须要用户或测试人员调用,他们只须要调用总的接口便可,以此来保证安全性。算法


计算模块接口的设计和实现过程

在这里咱们首先设计了一个Solver类用于实现对单词个数最多字符串和单词长度最长字符串的计算,Node类实现存储计算中所须要的各个单词的节点数据。编程

算法的大体思路以下:安全

考虑这是一个特殊的有向无环图的最长路径问题(无环状况下),咱们能够将全部首先将全部的单词进行拓扑排序,按排序后的顺序可使用动态规划的算法实现寻找出最长的路径。而对于有环的状况,咱们使用了有剪枝策略的DFS搜索算法查找出最长单词链。函数

独到之处:无环状况下,按单词首字母和尾字母将单词分为26类,减小了单词数量较多时的排序时间。性能


  • 计算模块部分各个实体间关系以下:


计算模块接口部分的性能改进

  • 如下为无环,单词数量为10000的运行状况

从图中能够看出占用时间最长的是计算出文本中的最长单词链的函数,在这里咱们考虑使用动态规划算法来减小计算模块所花的时间。单元测试

  • 如下为有环,单词数量为74的运行状况

而对于有环的状况,咱们这里就实现了一个基础的DFS算法,并采起了必定的剪枝策略,好比记录已经遍历过的点它的最长路径,而后当遍历到这个点的时候,判断一下当前长度+当前点的最长路径长度,若是小于当前发现的最长路径长度,那就不须要继续遍历下去了,由于当前节点所能到达的最长路径仍然小于当前最长路径。用这些剪枝策略,减小了一些递归遍历,必定程度上优化了算法。学习


  • 有关Design by ContractCode Contract 的一些思考

Design by ContractCode Contract的定义可在连接中查看。

Design by Contract,指的是契约编程。在面向对象编程中,“接口”是咱们着重要关注的部分,面向对象编程就好像打磨好一个个小部件,而这些部件须要经过“接口”链接配合,成为一个总体。然而,当前的部件是否须要对以前的部件的正确性负责呢?或者说,若是以前的部件出现错误,当前部件是否要对这种错误进行补救措施呢?契约编程解决了这种问题。

所谓“契约”,就好像各个部件之间作好的约定通常:若是你传递给个人输入是不符合个人接口规范的,那我有权直接拒绝(结束程序),即方法的输入有必定的约束和指望。这种“契约”所保证的是,确保当前部件起码具备正确的输入,若是输入是不符合接口要求的,只能说明以前的部件存在bug。

契约编程的好处是保证了程序的健壮性,合理地“分摊责任”,使得编程人员在分工时,仅须要对本身的模块按照契约负责,并对输入提出必定的要求限制便可。考虑了契约,才能够保证面向对象的可靠性和可扩展性。

在本次结对编程中,咱们的计算模块(即Core)中的接口,对调用时传入参数进行了限制,采用了“契约编程”的模式,若是传入参数不符合接口的要求,说明调用者存在问题,没有遵循契约,所以能够经过assert跳出,撕毁契约。在单元测试中,咱们一样也使用assert来进行测试。但最终完成测试后,从程序性能的角度出发,在已经保证各部件接口正确的状况下,咱们删除了assert来提升效率。


计算模块单元测试展现

在测试计算模块的以前,咱们首先测试了一下命令行参数处理模块是否正确:

TEST_METHOD(TestMethod1)
        {
            // TODO: 在此输入测试代码
            argc = 3;
            argv[1] = "-c";
            argv[2] = "input.txt";
            tag = -1;
            headCh = '\0';
            endCh = '\0';
            isRing = false;
            filename = std::string();
            getopt(argc, argv, tag, headCh, endCh, isRing, filename);

            Assert::AreEqual(1, tag);
            Assert::AreEqual(headCh, '\0');
            Assert::AreEqual(endCh, '\0');
            Assert::IsFalse(isRing);
            Assert::AreEqual(0, filename.compare("input.txt"));
        }

        TEST_METHOD(TestMethod2)
        {
            // TODO: 在此输入测试代码
            argc = 3;
            argv[1] = "-w";
            argv[2] = "input.txt";
            tag = -1;
            headCh = '\0';
            endCh = '\0';
            isRing = false;
            filename = std::string();
            getopt(argc, argv, tag, headCh, endCh, isRing, filename);

            Assert::AreEqual(0, tag);
            Assert::AreEqual(headCh, '\0');
            Assert::AreEqual(endCh, '\0');
            Assert::IsFalse(isRing);
            Assert::AreEqual(0, filename.compare("input.txt"));
        }

相似的TEST_METHOD咱们写了6个,用以测试各项参数读入处理后,tag、headCh、endCh、isRing和filename是否正确。

而后,咱们又对计算模块的单词个数最多字符串单词长度最长字符串这两个方法进行了单元测试:

  • 单词个数最多字符串int gen_chain_word();
TEST_METHOD(TestMethod1)
        {
            // TODO: 在此输入测试代码
            int wordIndex = 0;
            char **result;
            char headCh = '\0';
            char endCh = '\0';
            bool isRing = false;
            char *wordlist[100];

            wordlist[0] = new char[20]{ "annzcclv" };
            wordlist[1] = new char[20]{ "klebwukqbui" };
            wordlist[2] = new char[20]{ "qhqkibinpyew" };
            wordlist[3] = new char[20]{ "fkapwouje" };
            wordlist[4] = new char[20]{ "mitecsqa" };
            wordlist[5] = new char[20]{ "mogowquzdsmto" };
            wordlist[6] = new char[20]{ "oxkyhmgemdfpq" };
            wordlist[7] = new char[20]{ "hzvreibfb" };
            wordlist[8] = new char[20]{ "phgxdlmyrw" };
            wordlist[9] = new char[20]{ "kuckfwlghglua" };
            wordlist[10] = new char[20]{ "ucqavnwkqseyy" };
            wordlist[11] = new char[20]{ "quhxkzqxf" };
            wordlist[12] = new char[20]{ "iwoegjfbxhu" };
            
            wordIndex = 13;
            result = new char*[wordIndex];

            int ans = gen_chain_word(wordlist, wordIndex, result, headCh, endCh, isRing);
            
            Assert::AreEqual(4, ans);
            Assert::IsFalse(std::strcmp(result[0], "mogowquzdsmto"));
            Assert::IsFalse(std::strcmp(result[1], "oxkyhmgemdfpq"));
            Assert::IsFalse(std::strcmp(result[2], "quhxkzqxf"));
            Assert::IsFalse(std::strcmp(result[3], "fkapwouje"));
        }
        
TEST_METHOD(TestMethod3)
        {
            // TODO: 在此输入测试代码
            int wordIndex = 0;
            char **result;
            char headCh = '\0';
            char endCh = '\0';
            bool isRing = false;
            char *wordlist[100];

            wordlist[0] = new char[20]{ "annzcclv" };
            wordlist[1] = new char[20]{ "klebwukqbui" };
            wordlist[2] = new char[20]{ "qhqkibinpyew" };
            wordlist[3] = new char[20]{ "fkapwouje" };
            wordlist[4] = new char[20]{ "mitecsqa" };
            wordlist[5] = new char[20]{ "mogowquzdsmto" };
            wordlist[6] = new char[20]{ "oxkyhmgemdfpq" };
            wordlist[7] = new char[20]{ "hzvreibfb" };
            wordlist[8] = new char[20]{ "phgxdlmyrw" };
            wordlist[9] = new char[20]{ "kuckfwlghglua" };
            wordlist[10] = new char[20]{ "ucqavnwkqseyy" };
            wordlist[11] = new char[20]{ "quhxkzqxf" };
            wordlist[12] = new char[20]{ "iwoegjfbxhu" };

            wordIndex = 13;
            result = new char*[wordIndex];
            headCh = 'k';

            int ans = gen_chain_word(wordlist, wordIndex, result, headCh, endCh, isRing);

            Assert::AreEqual(3, ans);
            Assert::IsFalse(std::strcmp(result[0], "klebwukqbui"));
            Assert::IsFalse(std::strcmp(result[1], "iwoegjfbxhu"));
            Assert::IsFalse(std::strcmp(result[2], "ucqavnwkqseyy"));
        }
  • 单词长度最长字符串int gen_chain_char();
TEST_METHOD(TestMethod2)
        {
            // TODO: 在此输入测试代码
            int wordIndex = 0;
            char **result;
            char headCh = '\0';
            char endCh = '\0';
            bool isRing = false;
            char *wordlist[100];

            wordlist[0] = new char[20]{ "annzcclv" };
            wordlist[1] = new char[20]{ "klebwukqbui" };
            wordlist[2] = new char[20]{ "qhqkibinpyew" };
            wordlist[3] = new char[20]{ "fkapwouje" };
            wordlist[4] = new char[20]{ "mitecsqa" };
            wordlist[5] = new char[20]{ "mogowquzdsmto" };
            wordlist[6] = new char[20]{ "oxkyhmgemdfpq" };
            wordlist[7] = new char[20]{ "hzvreibfb" };
            wordlist[8] = new char[20]{ "phgxdlmyrw" };
            wordlist[9] = new char[20]{ "kuckfwlghglua" };
            wordlist[10] = new char[20]{ "ucqavnwkqseyy" };
            wordlist[11] = new char[20]{ "quhxkzqxf" };
            wordlist[12] = new char[20]{ "iwoegjfbxhu" };

            wordIndex = 13;
            result = new char*[wordIndex];

            int ans = gen_chain_char(wordlist, wordIndex, result, headCh, endCh, isRing);

            Assert::AreEqual(4, ans);
            Assert::IsFalse(std::strcmp(result[0], "mogowquzdsmto"));
            Assert::IsFalse(std::strcmp(result[1], "oxkyhmgemdfpq"));
            Assert::IsFalse(std::strcmp(result[2], "quhxkzqxf"));
            Assert::IsFalse(std::strcmp(result[3], "fkapwouje"));
        }
TEST_METHOD(TestMethod9)
        {
            // TODO: 在此输入测试代码
            int wordIndex = 0;
            char **result;
            char headCh = '\0';
            char endCh = '\0';
            bool isRing = false;
            char *wordlist[100];

            wordlist[0] = new char[20]{ "rlqokvxuq" };
            wordlist[1] = new char[20]{ "vvitmqskdyeap" };
            wordlist[2] = new char[20]{ "llkgasgiuzlgx" };
            wordlist[3] = new char[20]{ "cxadwktc" };
            wordlist[4] = new char[20]{ "yinrlisikdjq" };
            wordlist[5] = new char[20]{ "cbrcxzoyigcv" };
            wordlist[6] = new char[20]{ "roeuzja" };
            wordlist[7] = new char[20]{ "pwwbogbwp" };
            wordlist[8] = new char[20]{ "rjztssi" };
            wordlist[9] = new char[20]{ "vypbjouumrc" };
            wordlist[10] = new char[20]{ "vgorbjxqpap" };
            wordlist[11] = new char[20]{ "vrczrlwavkfq" };

            wordIndex = 12;
            result = new char*[wordIndex];
            isRing = true;

            int ans = gen_chain_char(wordlist, wordIndex, result, headCh, endCh, isRing);

            Assert::AreEqual(5, ans);
            Assert::IsFalse(std::strcmp(result[0], "vypbjouumrc"));
            Assert::IsFalse(std::strcmp(result[1], "cxadwktc"));
            Assert::IsFalse(std::strcmp(result[2], "cbrcxzoyigcv"));
            Assert::IsFalse(std::strcmp(result[3], "vvitmqskdyeap"));
            Assert::IsFalse(std::strcmp(result[4], "pwwbogbwp"));
        }

与测试命令行参数处理模块相似,咱们对gen_chain_wordgen_chain_char进行了headCh、endCh和isRing这几个参数的各类状况的测试,充分考虑了各个分支,达到了比较好的测试效果。最终咱们的整体覆盖率以下:


计算模块异常处理说明

WordRingsException:当默认状况下输入出现单词环时抛出
IllegalInterfaceParaException:当调用接口时传入不合理的参数时(如len <= 0,head或tail既不为'\0'也不是字母时)
IllegalParametersException:命令行参数出现错误时
FileNotExitException:输入的文本不存在时


命令行模块设计

命令行处理咱们采用最简朴的字符串比较方法,依次对参数进行分析:

对于整体咱们处理了不含有“-w”“-c”的异常(由于此时咱们不知道该如何选择最长字符串),同时在每个分支中,咱们也都进行了异常处理,如既有“-w”又有“-c”的,或是“-h”“-t”后面没有字母或是否是字母字符等等状况。

命令行处理模块将从控制台读入的信息处理好后,将信息储存在tag,headCh,endCh,isRing和filename中,供getFileInput()方法调用。


命令行模块与计算模块的对接

命令行模块引入了定义接口的头文件,直接能够经过需求,调用定义的两个接口中的一个,实现计算最长单词链的过程。


  • 结对编程的美妙过程

总的来讲本次结对编程的体验很不错,开始很顺利,过程当中虽然遇到了或大或小的麻烦,但咱们共同克服,最后写出的项目质量也还不错。

刚开始的时候,咱们还不是很适应“驾驶员”、“领航员”的模式,基本就是商量着写代码,但效率也还不错,设计的时候两我的相互补充,也避免了未考虑到某些方面的状况发生。很快咱们就将“有向无环图”的状况完成,并进行了测试,性能还不错。不过以后的“有向有环图”的设计就陷入了僵局,咱们除了暴力深度搜索想不出什么比较好的方式(事实是咱们最后也仍是暴力搜索),一度两人都在摸鱼。不事后来,咱们决定先将其完成,而后再进行剪枝来优化(毕竟测试数据集也不是很大),很快也将“有向有环图”的状况完成。最后的单元测试和错误处理模块,咱们也很顺利地完成。

整体来讲此次结对编程让咱们体验到了这种新的编程模式,可以和队友在交流碰撞,产生新的想法和点子,互相纠正、互相补充,这种经历十分难得,于我我的而言也是一次很大的提高。不过,这都是创建在我和个人队友是平日关系密切、很熟悉的前提下,若是在公司中采起这种方式,我就不是很确定效率会如何了。

最后,奉上结对编程留念图:


有关结对编程的一些感悟

邹欣大大的有关“结对编程与两人合做”的文章镇楼!!

由于以为这种编程方式是一个颇有趣的事情,因此在写本次结对编程做业的时候也是严格按照两我的一块儿编码的方式进行的。体验下来仍是有一些感想的:

  • 结对编程的优势:
  1. 一我的编码,一我的思考设计+复查,能够高效推动且保证较高的准确率
  2. 两我的不断交换身份,也可使得编程不那么疲惫,保持高效工做状态
  • 结对编程的缺点:
  1. 因为本次个人队友就是个人室友,十分熟悉因此不须要磨合,但现实状况下,可能磨合起来仍是比较麻烦,甚至可能出现两我的就是不合适的状况
  • 评价队友
    • 优势:
      1. 代码能力极强
      2. 思路清晰活跃,是一名优秀的“领航员”
      3. 为人比较细心,考虑周全
    • 缺点:
      1. 编码手速较慢,有时候看得我好难受...
  • 自我评价
    • 优势:
      1. 执行力强,编码速度快,虽然有可能存在失误,但有优秀的队友兜着我
      2. 比较细心,在作复查的时候能够帮助队友发现一些小的手误
      3. 与人为善,和队友十分开心嘻嘻嘻
    • 缺点:
      1. 头脑远不如队友清晰,思路有时候跟不上


PSP表格回填

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 20
· Estimate · 估计这个任务须要多少时间 30 20
Development 开发 720 890
· Analysis · 需求分析 (包括学习新技术) 90 120
· Design Spec · 生成设计文档 60 30
· Design Review · 设计复审 (和同事审核设计文档) 30 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 40
· Design · 具体设计 120 120
· Coding · 具体编码 210 360
· Code Review · 代码复审 60 50
· Test · 测试(自我测试,修改代码,提交修改) 120 150
Reporting 报告 90 100
· Test Report · 测试报告 30 40
· Size Measurement · 计算工做量 30 20
· Postmortem & Process Improvement Plan · 过后总结, 并提出过程改进计划 30 40
合计 840 1010
相关文章
相关标签/搜索