2021年软工 结对项目第三阶段总结

教学班级:2021春季软件工程(罗杰 任健)

GitLab项目地址:https://gitlab.buaaoo.top/2021_alige_homeworks/pair_works/2021_shen_guo-shiyu_ding-tianchun_gan_pair_work

结对人员(学号后四位): 3584 ,3622

一、结对项目实践反思

1.1 问题综述

​ 仅就在测试中出现的Bug而言,大多数Bug都是由于测试不充分导致的(部分路径未覆盖或压力测试的缺失),而这种测试的不充分又是由于时间的紧迫性导致的。我们在分析与编码过程中花费了过多的时间,导致留给测试的时间严重不足,且心理上比较疲乏,最终往往是针对覆盖率写单元测试就万事大吉。而这种在编码过程中花费过量时间的原因,既有对指导书的分析上的,也有架构设计上的,也有具体编码过程中的。

1.2 需求分析实践体会

​ 在这一部分提出一点G同学的个人看法,欢迎助教不吝赐教。

​ 在我看来,软件工程的需求分析是一个开发人员经过调研与分析,准确理解用户和项目的功能、性能、可靠性等具体要求,并将用户的非形式的需求表述转化为完整的需求定义的过程,它强调的是将不明确的需求明确化,甚至帮助用户发现自己都没有意识到的需求的过程。而观察课程组所给出的指导书,可能是出于评测方便的考虑,已经明确给出了各条指令的具体实现方式、异常顺序等一系列内容,观感上比较类似一个规格说明书,对这样的规格说明书不存在所谓需求分析的过程。因此我提出这样一个观点,本次项目的需求分析是由课程组完成的,而结对团队仅是拿到需求分析的结果并进行实现而已。是课程组对于“基于内存的文件系统”这样一个比较宽泛的需求进行了分析,设计了数条指令,规范了指令行为,才形成了指导书,并交予结对团队进行实现。

​ 很多同学都认为第二阶段的指导书让自己体验极差(例如这里),在我看来,这正体现了需求分析对于软件工程全过程的重要性,正是由于指导书(这里仍将指导书理解为需求分析产生的结果)在细节上存在比较多的问题(具体问题将在3.4节中详述),才导致了负责实现的结对团队难以在较短时间内完成一个比较好的版本,且开发体验不佳。当然这并不是在甩锅给课程组,在改革的第一年就形成这样体量的指导书已经是比较大的工作量了。

1.3 架构设计实践体会

​ 在本次结对作业的过程中,我们在架构设计与迭代上出现了比较大的问题。

​ 在第一次指导书发布后,我们首先进行了分别阅读,并由H同学初步设计了整体架构,在此设计中,我们用一个全局的HashMap保存文件系统中的所有文件,实现了一个与文件系统无关的将任意绝对或相对路径转化为一个完美的绝对路径getAbsolutePath方法,文件系统的每个方法都先调用此方法以转化输入的路径后直接从全局Map中取得实体,虽然这样做能满足第一次作业的全部要求,甚至还可以简化文件树结构(不需要存父节点),简化文件系统具体方法实现方式,但实际上已经埋下了巨大的隐患。

​ 击溃这一架构的重要指令便是mkdir -p,由于其路径中出现的部分可能在文件系统中不存在,加之一些其他类似\结尾的问题,导致了我们前文中设计的工具类方法的有关特判大大超出了预期,复杂度过高,代码难以阅读和维护,也难以针对指导书随时的修改而快速修改代码实现,因此我们在讨论后决定更改为实现一棵完整的文件树,将路径视作分步在树上移动的方式。

​ 在更换架构,重写工具类的过程中,我们一心想着赶进度,觉得在第一阶段中构造的myFileSystem各方法的测试已经较完备地覆盖了工具类,因而没有针对新增的TreePath类进行单元测试,两人都默认了这种方式的后果就是此新增类直接忽视了路径的4096字符限制,在第三阶段的测试中造成了错误。

​ 架构的设计是软件工程开发阶段的基石,具有举足轻重的地位,结合自己在此次作业中出现的问题,我们提出以下两点:

  • 在确定架构之前需要进行完整、详细的设计与复审,对于每一个细节都应该进行调研或测试。在结对设计架构的过程中,常常出现双方各执一词的情况,此时不适宜采用一人拍板定论的方式,而应该各自排出论据,详细讨论优劣,以期说服另一方,最终达成一致。
  • 若出于各种原因对架构做出了改变,则对新增的工具类等需要做详细无遗漏的单元测试,保证通过回归测试。架构的变化往往会删除和新增一些重要的工具类,这些工具方法复用度极高,需要优先保证正确率。在结对编程过程中,对这些类与方法要特别注意,必须进行同步复审。

1.4 质量管理实践体会

​ 进度管理上,我们采用了一种类似燃尽图的形式。以第二阶段为例,我们将主要编码工作分为修改数据结构和工具类,增加用户系统类并实现与文件系统交互,实现用户有关指令,实现用户组有关指令,实现硬、软连接指令,实现复制和移动指令等六个阶段,虽然没有按时间顺序具体将燃尽图画出来(考虑到从指导书发布到截止提交只有数天的时间,在真正的软件开发过程中可能只够一次“冲刺”),但我们对已经完成和还未完成的工作量都有了一个大概的认识,有助于估计仍需耗费的时间,便于及时调整进度。但是,在此过程中的一个问题是,我们对每一阶段的任务量估计不够准确,因而没有做到尽量等量划分任务单元(如第五和第六阶段的任务量其实比前四个加起来都多),最终造成了开发时间上的延误,对测试阶段造成了一定的影响。在之后的团队项目中,将继续尝试使用燃尽图方式进行进度管理。

​ 质量管理上,我们在代码实现中使用了很多TODO块。在等待Issue区的助教盖棺定论过程中,亦或是对细节有不清楚的地方而暂时不便打扰另一方时(如在实现一个逻辑比较复杂的方法,需要沉思),亦或是可能需要抽象出重复代码或做性能上的优化时,我们都会先在对应地点加上一个TODO,并详细注明TODO的理由和内容,在一次小的“冲刺”结束后,会针对现有代码中的所有TODO进行讨论,最终达成一致。

图1 分别由H同学和G同学写的TODO示例

​ 此外,我们还尝试过在具体实现代码之前先针对复杂方法写类似JavaDoc的注释的方式,但我们最终发现这种方式效率比较低,且可以被单元测试样例所取代,没有达到预期的效果,遂放弃。

图2 G同学写的某方法的注释

​ 沟通管理上,由于二人可以随时线下交流,因此省去了很多的无效交流时间。对于一些对指导书理解不一致的地方,或者通过Issue区的助教回复,或者通过Ubuntu的现场实验,最终都能达到统一的理解,因而没有在这方面上出现Bug。

1.5 相关建议

​ 《构建之法》中指出:“在结对编程模式下,一对程序员肩并肩、平等地、互补地进行开发工作。他们并排坐在一台电脑前,面对同一个显示器,使用同一个键盘、同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起做单元测试,一起做集成测试,一起写文档,等等”。然而,在结对编程的实践过程中,可以不拘泥于以上的形式和内容,保证适当的独立性也是可以接受的。结对编程的目的是提高工作效率和降低错误率,不需要为了结对编程而结对编程。具体而言,可以尝试使用以下方式进行结对项目的实施与管理:

  • 在合适的时候“分而治之”。虽然结对编程的随时复审特性能极大降低错误率,但它不是万能的,在部分编码过程中实行先分开实现再讨论的传统方式不仅能提高工作效率,而且对错误率不会产生太大的影响。本次项目的第一阶段我们全程结对完成,而在第二阶段,我们在实现具体指令过程中尝试了“一人写测试样例,另一人实现对应方法,下一个方法再进行转换”的方式,这样的方式很契合与文件系统类似的指令之间没有耦合的需求,虽然没有结对,但同一个方法经过了两个人的代码实现和单元测试样例构造过程,只要该方法通过了单元测试,我们就可以认为二人理解已经一致,无形之中完成了复审。
  • 保持良好的沟通。与一些其他组进行交流后我们发现,在结对项目的过程中,一个很大的阻碍便是失联的队友,在队友失联情况下个人完成的代码质量得不到保障,更严重的是会对之后的进度造成影响,如出于对代码熟悉程度的原因再也不放心让队友接手写这一部分的其他代码。在这一点上我们这次做的比较好,大部分时候都处于线下结对状态,在线上完成的针对Issue的小补丁也都及时进行了通知,使得二人始终对所有代码都有较高的熟悉度。

二、CI体验感想

​ 整体而言CI的使用体验较好。虽然在第一次作业提交前花费了一些时间用来配置CI,但在之后的每次提交基本上都不需要对其做出太大的改动。另外,需要感谢my同学提出的#Issue12中对CI单元测试和导出覆盖率的方法分享。

​ 本次项目中的CI仅涉及了build,test,commit等流程,而没有涉及到部署或发布等流程,因此其主要便利之处在于能自动进行单元测试并生成覆盖率报告。能将一部分工作交由CI自动完成,使得我们有更多的时间对代码本身进行思考。

三、结对编程感想

3.1 结对方法评价

​ 本次结对项目中我们主要采用线下实时使用Code With Me插件协作的方式,并且我们认为这种方式很适合结对编程的需求。线上改动代码的情况少之又少,仅在写单元测试的时候出现过一段时间的分治。

​ 与第一阶段总结中提到的一样,经过一整个结对项目的实践,我们认为,结对编程最主要的优势就是其随时复审的特性会使得低级错误的出现率大大降低。在较长时间的编码后,编码者往往会犯一些低级错误,如将 absolutePath 误写为 path ,将 && 误写为 || 等,这样的错误往往在测试前难以发现,且经过漫长的 debug 后发现低级错误也会极大增加挫败感。而两人共同面对同一份代码时同时犯低级错误的概率将远低于单人,节省了大量的测试时间。结对编程的主要缺点就是当两人对某一具体细节各执一词时可能会消耗大量时间用于讨论和争辩,从而造成开发效率上的问题,这一点在面对第二阶段的指导书时尤为严重,我们经常针对到底改不改父目录或子目录的modify_time等问题进行争论,难以盖棺定论,为此消耗了大量时间,延误开发进度。

​ 结对“妙招”已在1.5节中给出,在此不再赘述。

3.2 结对队友评价

Gottfried → Hastune

​ H同学思维敏锐,执行力强,在简要阅读指导书后就可以快速形成基础架构。但是在具体编码过程中犯低级错误的频数有些过高,如同一段代码中上面使用的还是absolutePath,下面就变成了path。如果可以做到形成一套自己的变量命名逻辑,在动手写代码之前想清楚代码目的,写完后先进行一遍自我复审,将极大地提高代码质量,从而缩减debug时间。

​ 丁酱,我的超人。

Hastune → Gottfried

​ G同学无论是在代码的思考构建过程中,还是在代码的编写过程中,都给人一种很细腻的感觉。讨论架构的时候,他会就某一点询问其它方式的可行性,只有在权衡所有方法的优劣之后,他才会做出相应的决策。例如有一次在讨论路径的存储结构应当是字符串还是数组时,我一开始提出应当用字符串进行存储,以便进行更加灵活的处理。但是他首先询问了数组存储的可能性,虽然我一开始觉得还是字符串存储较好,但是在听他说明了数组处理的效率高的特点以及数组结构与路径结构的相匹配性后,我最终决定采用他所说的方法。在我编写代码的过程中,他也会指除我某些逻辑方面的漏洞以及提出更加合理的解决方案。

​ 不过与G同学一起编写代码的过程中,我也发现了他对所使用的编程语言的语法与结构熟悉程度不太高,以至于无法写出一些比较常规的操作。还有就是他在结对编程的过程中表现得不太积极,大部分都是我主动去邀请他进行结对编程,而且他有时还会在结对过程中去偷偷写案例分析作业,这让我很恼火。😅

​ 虽然我指出了他的部分不足之处,但是瑕不掩瑜,在合作过程中他也教会了我很多东西,很期待下一次再与他一起合作!

3.3 结对有关工具简介

​ 本次结对编程实践过程中,我们主要使用了以下工具:

  • IDEA,不予详述
  • Code With Me插件,详细介绍请参见结对编程第二阶段总结博客的第一部分
  • git,除开提交以触发评测之外,还运用它进行适当的版本管理,以应对随时变化的指导书
  • Ubuntu18.04(虚拟机),用于查看所给出的指令在Ubuntu上的执行结果
  • JUnit + cobertura,进行单元测试并生成覆盖率
  • XMind,用于绘制思维导图等一系列图,便于讨论具体架构

3.4 对结对作业的建议

​ 在前文中“指导书是需求分析的产物”的观点的基础上,在这里,想对指导书做出一些具体的建议,整体上,期望课程组能多从读者(即结对团队)的角度看待指导书:

  • 取消对于异常“以下不再赘述”的说法。或许是出于不想让一条指令的描述过于冗杂的想法,指导书中存在大量“以下不再赘述”的异常,虽然这样确实能够节省文字,但是对于读者而言却造成了一定的困扰,结对团队需要在实现具体方法之前先扫描整个指导书,看看有没有可能遗漏的“不再赘述”的异常,更不用说“以下不再赘述”加上“指导书中该类型异常首次出现的位置越靠前,则它的优先级越高”的两条描述合在一起会造成更大的迷惑性。因此我认为这种做法弊大于利,可以考虑更改为在每一处需要异常的地方复制一遍该异常描述的方式,这样结对团队在实现某一具体方法时可以聚焦于该方法中,提高工作效率。
  • 以更准确的形式给出复杂指令的具体需求,取消“视作修改一次xxx”的表述。通过与部分同学进行交流我们发现,第二阶段指导书中的movecopy指令带来的迷惑性尤为严重,在Issue区中也出现了大量的类似于“到底改不改父目录的modify_time”,“覆盖文件究竟继承谁的create_time”等问题。在我看来,这些问题很大程度上是“视作修改一次xxx”的表述不能令人满意所导致的,可以考虑更改为#Issue15中my同学所使用的表格的形式。

图3 复杂指令的描述表格样例
  • 取消“对于指导书未提及的指令行为,以标准 Ubuntu 18.04 系统作为参考”的策略。我认为,作为一个软工项目,需求是要和一个现有的软件保持一致是一件很奇怪的事情。指导书(仍认为是需求分析的产物)就是“圣经”,结对团队需要按照指导书的描述进行实现,而不是同时还要考虑和另一个软件的对齐情况。更何况,在读者看来这句话有点类似于兜底罪,在存在不公开的强测的情况下会弄的人心惶惶,造成不必要的麻烦。

​ 以上给出了一些具体的针对指导书的建议,但是如果我们跳出这一限制,展开想象,也可以给出一些七七八八的可能是异想天开的其他建议:

  • 给出结对团队能自行设计输入并得到标程给出的输出结果的接口。在本次结对过程中,很多时间都被花费在争论小的指导书细节/迷惑处和等待助教回复Issue上,如果课程组给出这样的标程接口,结对团队一试便知何为正确操作,将极大提高开发效率。但是这样做可能会存在反编译等一些风险,有待进一步讨论。

  • 给出更大的实现自由度。作为一个软工项目而言,我认为“指令形式的文件系统”这样的选题和“对输出一字不差的进行比对”这样的评测方式可能不太适合,反倒是比较类似OO的第三、四单元,对实现限制较大。类似OO第二单元的“合理即正确”的输出评测方式(虽然很大程度上是因为它是多线程)是否更适合软工课程呢?我目前也没有什么好的想法。

    ​ 以上两条仅是一些不成熟的想法,欢迎大家进行讨论。

posted @ 2021-04-09 02:01  Gottfriede  阅读(134)  评论(3编辑  收藏  举报