结对项目-最长单词链

2019年软件工程基础-结对项目作业

题目修改自现代软件工程作业 – 计算最长英语单词链

零、任务

实现一个计算最长英语单词链的程序

一、要求

  • 阅读《构建之法》第一章至第三章的内容,并在下方作业里体现出阅读后的成果。特别是第2章中的效能分析及个人软件开发流程(PSP)。
  • 使用Visual Studio Community 2017进行开发,采用C++或者C#语言实现,可以使用.Net Framework,运行环境为64-bit Windows 10。
  • 附:下载链接:Visual Studio 2015/2017,下载也推荐使用国内一些镜像站点(不是盗版)MSDN,I tell you
  • 提交的代码要求经过Code Quality Analysis工具的分析并消除所有的警告。
  • 完成项目的首个版本之后,请使用性能分析工具Studio Profiling Tools来找出代码中的性能瓶颈并进行改进。
  • 使用单元测试对项目进行测试,并使用插件查看测试分支覆盖率等指标;并写出至少10个测试用例确保你的程序能够正确处理各种情况。
  • 使用Github来管理源代码和测试用例,代码有进展即签入Github,签入记录不合理的项目会被助教抽查询问项目细节。
  • 按照要求发布博客,利用在构建之法中学习到的相关内容,结合个人项目的实践经历,撰写解决项目的心路历程与收获。博客与Github项目明显不符的作业将取消作业成绩。

二、作业提交:

  • 撰写一个博客,要求参见博客作业要求。
  • 在个人博客上发布项目源代码(包含单元测试用例)的Github链接,以便助教下载,助教将会在测试环境中检查程序的正确性。
  • 正确的程序会再进行性能测试,根据性能的好坏进行评分;不正确的程序没有性能的分数。

三、第一阶段目标:实现一个命令行的计算最长英语单词链的程序

实现一个命令行程序Wordlist.exe,对于包含有 N个不同的英语单词的文本,要求程序可以快速找出最长的能首尾相连的英语单词链,注意每个单词最多使用一次,且单词大小写敏感:

其中,单词的定义为:被非英文字符间隔的连续英文字符序列
单词链的定义为:由至少2个单词组成,前一单词的尾字母为后一单词的首字母,且不存在重复单词

例如,给出单词文本为:

Hello WoRld! Softw
are_eng1neer

从文本中可以提取出的单词为:

hello world softw are eng neer

在默认情况下,输入的单词文本需要保证无法构成英语单词环(即末尾单词尾字母为首个单词首字的母单词链),对于不满足该条件的单词文本,程序不应该求解,而是给予用户错误提示。

我们对最长的定义分为两种:最多单词数量最多字母数量

基本需求1:计算最多单词数量的单词链

在命令行中使用 -w 参数加文件名的形式计算最多单词数量的英语单词链,并将结果输出至文件,例如:

> Wordlist.exe -w absolute_path_of_word_list

程序将从路径中读取单词文本,并将最长单词链输出至与Wordlist.exe同目录的solution.txt中,每次生成的txt文件需要覆盖上次生成的txt文件。

假如可能有多组最长的相连英语单词串,选取其中任意一组作为结果即可

程序需要对异常情况进行处理,包括但不限于:文件不存在、文件不合法等,对于上述异常情况,程序需要向用户返回提示信息

输入示例

Algebra
Apple
Zoo
Elephant
Under
Fox
Dog
Moon
Leaf
Trick
Pseudopseudohypoparathyroidism

输出示例

将结果输出到文件中,每行仅包含一个单词,单词为小写,例如:

algebra
apple
elephant
trick

基本需求2:计算字母最多的单词链

在命令行中使用 -c 参数加文件名的形式计算字母最多的英语单词链,并将结果输出至文件,例如:

> Wordlist.exe -c absolute_path_of_word_list

输出示例

pseudopseudohypoparathyroidism
moon

基本需求3:指定单词链开头或结尾字母

在命令行中使用 -h 参数加字母的形式,指定单词链的首字母,例如:

> Wordlist.exe -h e -w absolute_path_of_word_list

输出示例

elephant
trick

在命令行中使用 -t 参数加字母的形式,指定单词链的尾字母,例如:

> Wordlist.exe -h t -w absolute_path_of_word_list

输出示例

algebra
apple
elephant

需要注意的是,-h-t 参数允许复合使用,此时需要同时满足首字母和尾字母的条件。

基本需求4:允许单词文本隐含单词环

在命令行中使用 -r 参数,表示允许单词文本隐含单词环,此时文本中每种单词只能使用一次(即使单词在文本中多次出现,也只能使用一次),如:

> Wordlist.exe -r -w absolute_path_of_word_list

输入示例

Element
Heaven
Table
Teach
Talk

输出示例

table
element
teach
heaven

此处table-element构成单词环,如果命令行中不包含 -r 参数,则此种情况应当做异常处理。

附加需求:用户交互界面绘制(10')

现在已经有了一个单词链计算程序的命令行版本,如果想让大家都能实际使用它,还需要一个简单的界面。请为你们的程序做一个GUI界面,并附上一个简单的使用说明。界面需正确实现下述功能,会按点给分:

  1. 支持两种导入单词文本的方式:①导入单词文本文件,②直接在界面上输入单词并提交(3')
  2. 提供可供用户交互的按钮和,实现-w -c -h -t -r这五个参数的功能,对于异常情况需要给予用户提示(3')
  3. 将结果直接输出到界面上,并提供“导出”按钮,将结果保存到用户指定的位置(3')

以上功能全部正确完成,可以获得10分满分。

【注意】选择完成本附加题目的同学,需要将GUI与单词计算模块作为两个工程开发,后者可以作为依赖库为前者提供调用接口,但不可以把两个工程直接混在一起。 GUI相关的部分也需要提供新的可执行文件,放在根目录的GUIBIN文件夹下。

四、第二阶段目标:把计算单词链的功能封装为独立模块,并设计单元测试

在第一阶段中,我们使用各种语言实现了一个命令行求解最长单词链的小程序。下面我们将逐步将我们的小程序升级为能稳定运行,给用户提供服务的软件。

大家的代码都各有特色,大家写的“软件”也有一定的用处。如果现在我们要把这个功能放到不同的环境中去(例如,命令行,Windows图形界面程序,网页程序,手机App),就会碰到困难:许多同学的代码都散落在各个函数中,很难把剥离出来作为一个独立的模块运行以满足不同的需求。

我们看到,不同的代码解决不同层面的问题:

  • 有些是计算数据的(例如计算单词链)
  • 有些是控制输入的(例如scanf,cin,图形界面的输入字段)
  • 有些是数据可视化的(例如printf,cout,println,DrawText)
    有些则更为特殊,是架构相关的(例如main函数,并不是所有的程序都需要某个特定格式的main)

这些代码的种类不同,混杂在一起对于后期的维护扩展很不友好,所以它们的组织结构就需要精心的整理和优化。

我们希望把计算单词链的功能能独立出来,成为一个独立的模块(class library, DLL, 或其它),这样的话,命令行和GUI的程序都能使用同一份代码。为了方便起见,我们称之为计算核心"Core模块",这个模块至少可以在几个地方使用:

  • 命令行测试程序使用
  • 在单元测试框架下使用
  • 与数据可视化部分结合使用

把计算核心在单元测试框架中做过完备的测试后,我们就可以在算法层级保证了这个模块的正确性。

但我们知道软件并非只有计算核心,实际的软件是交付给最终用户的软件,除了计算核心外,还需要有一定的界面和必要的辅助功能。那么这个Core模块和使用它的其他模块之间是什么关系呢?它们要通过一定的 API(Application Programming Interface) 来和其他模块交流。这个API接口应该怎么设计呢?为了简单,我们可以从下面的最简单的接口开始:

int gen_chain(char* words[], int len, char* result[]);

这个函数接受三个参数,words为输入的单词列表,len为单词列表的长度,result存放单词链,函数返回值为单词链长度。

假设我们用类Core封装了这个接口,我们的测试程序可以是非常简单的:

char* input[4] = {"END", "OF", "THE", "WORLD"};
char* result[4] = {0};
/* 调用Core中封装好的函数 */
int len = Core::gen_chain(input, 4, result);
Assert(len == 2);

当然,我们这里的判断并不充分,仅判断了单词链长度,没有判断单词链的合法性,但同学们在测试时不能这样“偷懒”。

我们要把第一阶段中实现的功能封装成独立的模块并一一进行测试,比如读取单词文本文件、输出打印等。建议大家在每一步只增量修改一个模块并做测试。这里的测试包括新模块的单元测试原功能的回归测试。每实现一个新的功能,要保证以前运行正确的例子继续是正确的。通过这样的回归测试,可以保证自己实现的系统始终是满足预定状态约束的。(请看书中关于单元测试,回归测试的内容)在确认修改的功能正确之后再签入代码。

项目要求:

  • 在第一阶段基础上增量修改,实现int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)接口,计算最多单词数量的最长单词链,其中前三个参数已经在上文进行了说明,headtail分别为单词链首字母尾字母约束(如果传入0,表示没有约束),当enable_looptrue时表示允许输入单词文本中隐含“单词环”
  • 实现int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)接口,计算最多字母数量的最长单词链,参数意义同gen_chain_word
  • gen_chain_wordgen_chain_char接口进行测试,把单元测试代码Push到Github上(注意避免把单元测试的结果Push到Github上)
  • 设计其他部分的接口,按照设计好的接口在个人项目基础上增量修改,同样把单元测试代码Push到Github上。
  • 在完成这一阶段的任务之后,使用git tag step1标记第一阶段已经完成,并在Push到Github上时使用--tags参数把tag也推送到Github,例如git push origin --tags

博客要求:

  • 详细介绍你对于上述Core接口的实现,以及你为Core模块设计的其他接口,并说明如何使用这些接口。
  • 选择部分单元测试代码发布在博客中,并说明测试的函数,构造测试数据的思路。
  • 将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到90%以上,否则单元测试部分视作无效。

五、第三阶段目标:通过测试程序和API接口测试对于异常处理的支持

在上面我们只讨论了正确的输入下,我们对于程序输出的期待。但如果程序的输入出现了错误,比如命令行参数是其他字符,或者有多个无意义参数等等,你又该怎么办呢?要怎样才能告诉函数的调用者“你错了”?又该如何方便地告诉函数的调用者“哪里错了”?在这种时候,我们一般会定义各种异常(Exception),让Core在碰到各种异常情况的时候,能给调用者充分的错误信息。当然,我们同样要进行增量修改:

项目要求:

  • 设计好异常的种类与错误提示,例如让程序支持“首尾字母约束不合法”或“单词文本隐含单词环”异常。
  • Core模块中实现抛出异常的功能,并撰写测试用例:传进去一个错误的参数或给出一个错误的单词文本,期望能捕获这个异常。如果没有,测试就报错。
  • 回归测试所有以前的功能,保证以前的功能还能继续工作。
  • 在完成这一阶段的任务之后,使用git tag step2标记第二阶段已经完成,并在Push到Github上时使用--tags参数把tag也推送到Github。

博客要求:

  • 在博客中详细介绍对哪些异常进行了处理以及每种异常的设计目标。
  • 每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。

六、第四阶段目标:界面模块,测试模块和核心模块的松耦合【附加题】

在前面的工作中,有的小组使用命令行,保证了程序的正确性,还有的小组在此基础上绘制了GUI界面。既然各组同学都写了高质量的各个模块,而且模块之间的关系是明确定义的,一致的,那么,小组A的测试模块就可以测试小组B的核心模块;小组C的用户界面模块就可以和小组B的核心模块结合起来,正常运行。对吧?

那么现在,请你(假设为A)寻找另外一个小组(假设为B),与他们交换核心模块与界面模块,并测试一下下面的情况:

  • A的核心模块,加上B的测试模块和用户界面模块(命令行或GUI)
  • B的核心模块,加上A的测试模块和用户界面模块(命令行或GUI)

项目要求:
根据与合作小组对接过程中出现的问题,寻找并改进模块中的bug。这部分修改需要另开一个新的分支dev-combine,并Push到Github上。

博客要求:
在博客中指明合作小组两位同学的学号,分析两组不同的模块合并之后出现的问题,为何会出现这样的问题,以及是如何根据反馈改进自己模块的。

七、测试须知

测试内容

我们都知道健壮性对于软件来说是非常必要的,所以本次自动测试我们也会加入各种各样出错情况的测试。助教测试时将会选择不同种类的出错场景,要求开发者程序不会崩溃的情况下,能够尽可能精确报错(就像编译器一样)。你可以有“容错性”的出错设计,但必须输出必要的提示或说明。

测试包括三部分:正确性测试(正确场景)、鲁棒性测试(错误场景)、性能测试,提交的所有程序都要进行正确性测试和鲁棒性测试,正确性测试通过的程序需要进行性能测试。

测试须知

所有提交到Github上的项目均需要建立一个名字为BIN的文件夹,里面必须含有计算核心生成的可执行文件与相关的依赖库,请注意以下三点:

  • 确保VS产生的临时文件和编译生成临时文件不被加入到Git代码仓库中(使用.gitignore文件管理哪些文件可以被忽略)。
  • 确保命令行测试程序的名字为Wordlist.exe,确保核心模块的DLL名字为Core.dll
  • 确保生成的棋盘文件solution.txt与可执行文件在同一目录下,生成文件时请使用相对路径!

一个示例组织目录如下所示:

/ WordProject(工程名字自行指定即可)
/ main.cpp
/ partA.cpp
/ partB.cpp
/ BIN
    / Core.dll(Core模块)
    / Lib.dll(运行需要的[其他]动态链接库文件)
    / Wordlist.exe
    / solution.txt (运行exe后生成)

八、评分细则

博文部分得分点

博客共五十分

序号 要求 分值
1 在文章开头给出Github项目地址 1
2 在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间 0.5
3 看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的 5
4 计算模块接口的设计与实现过程。 设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处 7
5 阅读有关UML的内容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。画出UML图显示计算模块部分各个实体之间的关系(画一个图即可)。 2
6 计算模块接口部分的性能改进。 记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展示你程序中消耗最大的函数 3
7 看Design by Contract, Code Contract的内容:
http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的
5
8 计算模块部分单元测试展示。 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到90%以上,否则单元测试部分视作无效。 6
9 计算模块部分异常处理说明。 在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。 5
10 界面模块的详细设计过程。 在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。 5
11 界面模块与计算模块的对接。 详细地描述UI模块的设计与两个模块的对接,并在博客中截图实现的功能。 4
12 描述结对的过程,提供非摆拍的两人在讨论的结对照片。 1
13 看教科书和其它参考书,网站中关于结对编程的章节,例如:
http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
说明结对编程的优点和缺点。
结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
5
14 在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。 0.5

注:结对小组中两个人发布独立博客,其中2)、3)、5)、7)、13)、14)部分请独立完成,不允许雷同。项目的测试分数两人共享,博客的分数各自独立。附加题的相关要求请按附加题的要求补充在博客中。

程序部分得分点

程序共六十分

源代码管理评分(5'):
该评分主要通过源代码管理中的commit注释信息,增量修改的内容,是否有运行说明,每个阶段是否打上了标签等内容给分。(5')

第一阶段(20'):

该评分将进行这-c -w -h -t -r五个参数的正确性测试,对于前四个参数,输入的单词数量范围为0-10000;对于-r参数,输入单词数量的范围为0-100,要求程序在 300 s 内给出结果,超时则认定运行结果无效

第二、三阶段(20'):

将针对上述六个参数进行鲁棒性测试,可能测试的内容包括且不限于:
错误的命令、错误的参数、大小写、错误的参数组合、错误的文件格式等。
要求必须正常结束,崩溃不得分。
错误无任何提示,不得分。
错误种类较多,提示合理,得正分。

性能评分(15')

当第一阶段评分等于20分时才可以参与性能评分环节,所以请各位同学务必保证自己程序的正确性,该阶段没有时间的最小要求限制。
性能评分将采取档级评分制度,助教将根据同学们的程序跑同一数据耗费的时间长度将程序分为若干档,每一档的同学得到的分数为 15/档级数。

附加题得分点

附加需求:GUI(10')
该评分将进行用户交互界面的测试

第四阶段:模块松耦合(10')
在结对项目博客中按照阶段四的博客要求添加相应内容(5') 最终的对接效果(5')

九、附录

PSP 2.1表格

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

参数约定

助教在测试时,将以命令行运行可执行文件的方式进行批量测试,参数及其约定如下:

参数名字 参数意义 范围限制 用法示例
-w 需要求出单词数量最多的单词链 绝对或相对路径 示例:Wordlist.exe -w input.txt [表示从input.txt中读取单词文本,计算单词数量最多的单词链]
-c 需要求出字母数量最多的单词链 绝对或相对路径 示例:Wordlist.exe -c input.txt [表示从input.txt中读取单词文本,计算字母数量最多的单词链]
-h 指定单词链首字母 a-z,A-Z 示例:Wordlist.exe -h a -w input.txt [表示从input.txt中读取单词文本,计算满足首字母为a的、单词数量最多的单词链]
-t 指定单词链尾字母 a-z,A-Z 示例:Wordlist.exe -t a -c input.txt [表示从input.txt中读取单词文本,计算满足尾字母为a的、字母数量最多的单词链]
-r 允许单词文本中隐含单词环 示例:Wordlist.exe -r -w input.txt [表示从input.txt中读取单词文本,计算单词数量最多的单词链,即使单词文本中隐含单词环也需要求解]

猜你喜欢

转载自www.cnblogs.com/slontia/p/10453507.html