• 软件工程博客作业2


    这个作业属于如下课程:软件工程
    这个作业的要求位于如下链接:结对编程
    我在这个课程的目标是:锻炼复杂软件开发能力,取得大于85分的成绩。
    这个作业在如下方面帮助我实现目标:快速上手C++语言;将结对编程、单元测试、添加注释、抛出异常等付诸实践。

    作业正文

    1、在文章开头给出GitHub项目地址。
    项目地址

    2、在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间。
    见附录部分。

    3、看教科书和其它资料中关于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.

    ——引用自维基百科"Information Hiding"
    我对Information Hiding比较有感触的是处理来自文件的输入的子程序。在第一遍实现时,我们将文件读入和解析的功能全部写在一个readFile函数中,鼓捣了半天也没调通。之后,我重写了这一部分代码:readFile函数只负责不停地以行为单位读取文件中的内容,而具体解析的工作交由另一个process_line函数完成。重写后的代码长度更短,而且调试花费的时间更少。在这个例子中,process_line向readFile函数隐藏了解析用户输入的信息。而且,一旦用户输入的格式发生变化,一般只需要修改process_line函数即可;代码的可维护性较好。

    Interface Design

    没有找到太多资料。核心模块的函数接口在作业要求的基础上略作修改(例如使用了vector和string),其余函数基本遵循如下原则:名称易于理解、参数尽可能少。

    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.
    Components in a loosely coupled system can be replaced with alternative implementations that provide the same services. Components in a loosely coupled system are less constrained to the same platform, language, operating system, or build environment.

    ——引用自维基百科"Loose Coupling"
    我们没有完成选做作业“松耦合”,对松耦合的支持不够好:我们将FileInput类的一个对象作为了wordHandler的成员变量,为之后写单元测试代码带来了一定的麻烦。更好的做法是让wordHandler仅拥有一个存有文件中所有单词的容器(例如vector)。事实上,wordHandler不关心也不需要了解文件读入的过程,而只需要拥有分好单词的文件内容。

    4、计算模块接口的设计与实现过程。
    在设计之初,为了降低代码单元间的耦合度,我们将整个程序拆分为3个类:FileInput读取用户文件,wordHandler寻找单词链,output将单词链以文件形式输出。这样的设计使得各个代码单元之间尽量分离,避免纠缠不清,造成修改困难。

    • FileInput类包含函数readFile(), process_line(),分别负责按行读取文件、解析一行字符串。
    • wordHandler类包含函数gen_chain_word(), gen_chain_char(), meet_req(), can_insert()
      • gen_chain_word()寻找单词数量最多的单词链;
      • gen_chain_char()寻找含有字母数量最多的单词链;
      • meet_req()检查命令行中对单词链首尾的要求是否被当前单词链满足;
      • can_insert()检查当前单词是否已经出现在链中;
    • output类包含函数tofile(),负责向文件中输出单词链。

    在算法上,我们采用蛮力算法进行求解。尽管在单词数量增多后难以取得令人满意的性能,但是能够返回最优解,而不是像某些算法只能返回近似解。

    5、画出UML图显示计算模块部分各个实体之间的关系。

    6、计算模块接口部分的性能改进。
    由于采用蛮力算法,且时间有限,我们未对计算模块进行性能改进。一个可能的方向是将所有单词排序,然后采用索引查找方法,以提高时间效率。
    性能分析图如下:

    7、看Design by Contract, Code Contract的内容;描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的
    契约式设计在同一个软件项目的开发人员之间形成契约:在前置条件下,程序员必须保证自己编写的方法维持了不变式内容,且满足后置条件。其优点是程序员可以认为前置条件为真而无需进行大量检查。其缺点是不遵守契约的程序员会为整个项目带来巨大破坏,而且程序员需要多花一部分时间撰写契约规格。
    本次作业中,我们未能使用契约式设计,造成了一些不良后果。一个典型的错误是某个变量究竟代表容器中元素的个数还是下标的最大值(二者差1)。这一点两人事先没有约定好,最终造成了BUG的发生。

    8、计算模块部分单元测试展示。

    • 测试代码
    
                    TEST_METHOD(TestMethod1)
    		{
    			FileInput fin;
    			fin.readFile("D:\word_chain\01.txt");
    			wordHandler wh(fin);
    			char *words[20];
    			vector<string> result;
    			wh.gen_chain_word(words, 0, result, 0, 0, false);
    			Assert::IsTrue(result.at(0) == "apple");
    			Assert::IsTrue(result.at(1) == "elephant");
    			Assert::IsTrue(result.at(2) == "toad");
    		}
    
    		TEST_METHOD(TestMethod2) {
    			FileInput fin;
    			fin.readFile("D:\word_chain\02.txt");
    			wordHandler wh(fin);
    			char *words[20];
    			vector<string> result;
    			wh.gen_chain_char(words, 0, result, 0, 0, false);
    			Assert::IsTrue(result.at(0) == "pseudonym");
    			Assert::IsTrue(result.at(1) == "moon");
    		}
    
    
    • 测试数据

    01.txt
    apple
    elephant
    toad
    方法1测试了gen_chain_word方法的正确性,情况设置得比较简单:在输入上没有设置干扰单词,且给出的单词顺序就是链的顺序。
    02.txt
    leaf, fox
    pseudonym, moon
    moon
    方法2测试了gen_chain_char方法的正确性,情况较为复杂:设置了重复的单词作为干扰项。
    我们还编写了其它单元测试代码,限于篇幅无法一一展示,请前往GitHub项目地址查看相应代码。

    • 测试覆盖率截图

    9、计算模块部分异常处理说明。

    • FINException:检测用户输入文件不合法时,抛出异常,main中捕获直接退出。

    10、界面模块的详细设计过程。
    按指导书中的命令行参数,设计了解释程序。如出现'-r',则将对应的bool型变量置为true,'-h'则按顺序读入下一个命令行参数,将其复制给char型变量,'-t'同上。'-w'先按顺序读入下一命令行参数作为文件路径,调用gen_chain_word()接口;'-c'同上,不同在于调用gen_chain_char()接口。读到文件路径后完成对命令行的解析。

    while (true)
    	{
    		con = false;
    		switch (ptr[1])
    		{
    		case 'r':
    			recur = true;
    			con = true;
    			break;
    		case 'h':
    			ptr++;
    			head = *ptr;
    			con = true;
    			break;
    		case 't':
    			ptr++;
    			tail = *ptr;
    			con = true;
    			break;
    		case 'w':
    			fun = 'w';
    			break;
    		case 'c':
    			fun = 'c';
    			break;
    		default:
    			break;
    		}
    		if (con)
    		{
    			i++;
    			ptr = argv[i];
    			continue;
    		}
    		else
    		{
    			break;
    		}
    	}
    	try
    	{
    		fin.readFile(argv[i + 1]);
    	}
    	catch (FINException& fine)
    	{
    		fine.what();
    		return 0;
    	}
    	if (fun == 'w')
    	{
    		wordHandler whword(fin);
    		whword.gen_chain_word(_words, _len, result, head, tail, recur);
    	}
    	else if (fun == 'c')
    	{
    		wordHandler whchar(fin);
    		whchar.gen_chain_char(_words, _len, result, head, tail, recur);
    	}
    	else
    	{
    		cout << "Invalid command!" << endl;
    	}
    
    

    11、界面模块与计算模块的对接。
    我们在main函数中实例化了FileInput, wordHandler两个类的对象,wordHandler的构造方法传入FileInput类的对象。main函数接受了命令行参数后,将识别的文件路径传入FileInput的相关方法,从而实现对接。

    12、描述结对的过程,提供非摆拍的两人在讨论的结对照片。
    我和杨亦鑫是隔壁班的同学,以前在打篮球时有过接触。课程开始后,我们分别进入了不同的团队。因此,我们符合结对编程的条件,经过简短的沟通后正式结对。
    我们结对编程的形式主要是先由我约一个两人都方便的时间,然后来到新主楼2层,找一个走廊里的座位,开始结对编程。
    由于是初次接触结对编程形式,我们在实际操作中出现了一定的问题。我在时间控制上不太敏感,经常是连续工作了一个多小时而忘记了交换角色。领航员有时候注意力开了小差,没有进行随时复审。不过两人合作的氛围较为轻松愉快,没有发生剧烈冲突。
    总的来说,这次结对编程并未提高我们的工作效率。但是,由于是两个人一块复审,我们最终还是发现了不少BUG,而且对于每个概念上的错误都能在事后牢记。这次体验对我的开发能力有所促进,但也提出了更高的要求。

    13、说明结对编程的优点和缺点。结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
    优点1:写代码和查API的工作可以交由两人并行完成,既提高了效率也形成了知识的共享。
    优点2:定时休息可以避免一个人长时间敲代码(这种情况常常导致BUG数增加)
    优点3:错误被发现时两人都留下了深刻的印象。
    缺点1:作为课程的一部分时,结对编程需要在两个学生都有空的时候进行,而且缺乏好的环境(大运村没有公共休息室),因此效果打了折扣。

    14、在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。
    见附录。

    附录

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划
    · Estimate 估计这个任务需要多少时间 15 4
    Development 开发
    · Analysis · 需求分析 (包括学习新技术) 180 120
    · Design Spec · 生成设计文档 240 0
    · Design Review · 设计复审 (和同事审核设计文档) 60 0
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 120 0
    · Design · 具体设计 480 20
    · Coding · 具体编码 1440 480
    · Code Review · 代码复审 480 180
    · Test · 测试(自我测试,修改代码,提交修改) 960 120
    Reporting 报告
    · Test Report · 测试报告 480 30
    · Size Measurement · 计算工作量 45 5
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 5
    合计 4560 964
  • 相关阅读:
    P4839 P哥的桶 题解(线段树维护线性基)
    线性基入门
    Lowest Common Ancestor 题解(lca+思维)
    B
    java string对象的简单方法
    AtCoder Grand Contest 016 D
    FFT
    回文自动机(BZOJ2565)
    二维RMQ
    AC自动机(BZOJ1030)
  • 原文地址:https://www.cnblogs.com/liqingyang/p/10526238.html
Copyright © 2020-2023  润新知