2021软件工程结对作业——第三阶段
项目 | 内容 |
---|---|
这个作业属于哪个课程 | 2021-春 软件工程(罗杰 任健) |
这个作业的要求在哪里 | 结对项目-第三阶段 |
这个作业的GitLab项目地址 | 2021年软工结对作业 |
我在这个课程的目标是 | 在结对项目和多人团队项目中,更加系统地学习软件开发,培养工程化的思维 |
这个作业在哪个具体方面帮助我实现目标 | 参与到结对作业中,体会结对编程 |
学号后四位 | 3197 3204 |
结对项目实践
1、实践中问题分析
- 任务分工不明确
结对一开始的时候,我们还是秉承着书中的建议:采用驾驶员和领航员不断互换角色的方式。
驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
——《构建之法》
但实际体验上,我们发现,并不是所有推荐方式都适合自己,只有经过实践检验才知是否适合自己。队友彼此之间思考方式和代码风格都存在比较大的差异。如cwm的代码风格偏向于面向过程,所以思考方式更局限于解决某一个方法;而wcf的代码风格颇有受到良好的OO课程教育的体现,相对而言更像是对整体框架的一个把握。所以一开始尝试角色互换时,虽然彼此对代码逻辑都很是理解,但是一旦两个人各自写的部分都很多的时候,整个程序会出现两种比较极端的风格。
在第一阶段的时候由于工程量不大,尚且还可以接受。到了第二阶段,从另一个人接受先前一个人写的代码的时候,就会发现逻辑差异较大。尤其是第二次作业中,文件系统操作中的mv,cp,ln -s,ln四条指令,代码逻辑上都有比较相近的地方,我们体验过顺着前面一个思路去思考而带来自己的逻辑错乱,时常陷入不知所措的地步。这个问题还是出在我们一开始没有明确的分工,虽然大体上还是有每个人主要负责哪一部分的意味,但实施起来,还是每个人在每一个部分都有不小的参与度。
经过我们的实践而得出的经历,如果是结对二者的代码风格存在明显差异的时候,更应该一个人为主,一个人为辅,就比如我们到了第二阶段后期,就采用了由wcf同学进行代码编写,cwm同学进行代码复审,并对细节部分进行反复验证;而测试部分由cwm同学进行主要测试,而由wcf同学对细节部分进行验证。
- 沟通不充分
第一阶段因为需求比较简单,很多细节在沟通中就可以很快地达成共识。到了第二阶段,细节的地方更多了,而且因为指导书的反复变化,使得有时候先前沟通所达成的共识又要打翻重新再来一遍。而沟通存在的问题,不仅仅是对细节寻找的不充分,还有细节上理解的不充分,以及沟通时双方表达能力的不充分。首先是细节上寻找的不充分,导致了我们在第二次强测中错了一个点:关于硬链接info信息的思考。我们都没有发现info一个硬链接实际要求的是显示除了路径为硬链接本身,其余均为指向文件的信息,最后复盘发现,我们都错误被前面的重定向带歪了,所以一看到路径为自己本身,就下意识地认为是将整体进行重定向。细节上理解的不充分和表达能力的不充分是指我们在进行单元测试的时候,反复修改了mv与ln -s这两条指令,其原因在于我们对dst和src分类讨论时候细节理解存在差异,而在差异上的讨论,一方面是双方讨论都很难深入理解,反而是反复重复着指导书的表面意思,另一方面就是,双方阐述自己的观点的时候,很多时候都是站在自己角度去阐释理解,而在另一个队友理解中,却是听得雨里雾里的。所以沟通不充分也是一个很大的问题。
- 时间比较零散
这一点其实也还是存在于二者一起协作的时间比较少,或者说我们很难找到一块很长的公共空闲时间来进行结对编程。还有一方面的原因就是双方都不太适应长时间的代码编写,所以我们整体上都是取零碎的时间如半个早上,半个下午,一个或半个晚上的时间进行结对编程,而到了晚上,尤其是23点之后,各自忙了一天都出现了疲态,而导致效率低下。所以整体上的时间比较零散,这就导致了每次开始编写是还得找回之前的感觉,从而一点程度上影响了效率。
2、需求分析实践体会
总结结对项目中的需求分析实践体会,并分析哪些bug是因为需求分析不足而带来的
结合书上第八章对分析和定义需求的论段:
这是指对从各个方面获取的需求进行规整,定义需求的内涵,从各个角度将需求量化:需求实现的最后期限,实现需求的大致所需的时间和资源成本,各个不同需求的优先级,需求带来的收益,等等
以及本次实践的经验来谈谈自己对需求分析的总结。首先,我们本次作业本质上和一个公司开发一个盈利项目不同,本次作业更多的是为了完成一个任务,而非应对真实用户的需求,所以这里暂时不用考虑收益问题。而本次结对任务的需求,可以简单理解为:指导书体现的即为需求。所以总结而言,这次的需求我们给出自己的理解:第一阶段:实现一个简单的文件系统,该文件系统能够模拟真实文件系统实现增删改查的需要;第二阶段:在第一阶段的基础上增量开发,增加用户系统以提供权限管理的需求,添加了软硬链接以及mv,cp等便捷操作的需求。
同时正如在书中1.2中所提及的:
开发的软件是可以维护和继续发展的
需求满足的同时,还需要考虑维护和扩展性。这一点在迭代开发一阶段的时候我们就考虑到了这个问题。所以第二阶段的对第一阶段的修改并没有拆锅炉重造,而是复用了原理的基础从而进行迭代开发。而仔细分析两个阶段的指导书我们会发现,本次结对作业最大的,也是最公共的需求就是模拟行为。指导书大部分给出的指令,其根本准则就是以Ubuntu18.04的行为,即使后面有一些不同,但整体还是围绕不开这个模拟这个需求。所以我们自己封装了cd,mkdir,enterfatherDir等函数来模拟行为。抓住这一点,无论是mv还是cp函数都是在ubuntu系统上对每个细节进行复现之后再在本地进行编写,总体上来说,需求分析没有偏离,也没有出现太严重的BUG,最后出现的BUG还是在于自己对行为没有仔细阅读而导致的。
3、架构设计体会
总结结对中的架构设计实践体会,描述通过改进设计来提高程序的性能改进的思路和方法,并分析哪些bug是因为架构设计不足(特别的,需求变化)而触发的bug;
首先我们最终在第二阶段DDL前将整体架构进一步完善,达到比较完整的组织架构。总的来说,在架构设计上,更多的还是依靠wcf进行设计的,由cwm进行一些补充。架构设计还是比较完整的,不管是迭代开发,还是对BUG进行定位,都是依靠良好的架构从而导致开发高效。
如图所示,我们展示了第二阶段所采用的大体架构模式。FileSystem和UserSystem通过Manger类进行交互,实现了两个系统较好的独立性(不过正是独立性太高了,以至于到最后才发现,对于用户系统的指令,我们忘记了来增加文件系统下的时间戳了,而这个BUG并未在弱侧中体现,在单元测试中,更多的测试也还是集中在各自系统中的测试,从而险些酿成大错)。而每一个系统都有比较独立的对象可以抽象出来,例如文件系统中的文件(File类),文件夹(Directory类),用户系统的用户(User类)和用户组(Group类)。而之后加入的软硬连接,又是比较特殊的类,这个时候我们可以让其继承File类,同时重载一些方法,并通过枚举类LinkType来表示链接的为文件还是文件夹。然后再回到mv,cp操作,这些操作回到本质上,还是对于文件夹,或文件的操作,所以一些方法可以放在对应的类中进行实现。而对于异常类的实现,我们尽可能的按照指导书所规定的异常类型对异常进行命名归类,从而使对应的结构更加清晰。
总的来说,一个好的架构可以使代码复用性提高,同时对于性能的优化可以根据寻求而具体到每一个类中的某一个方法。所以整体上来说,我们的架构设计比较好,对于原子性的保证也形成了自己的代码规范(官方实现的方法中,函数尹始进行当前目录保存,一旦遇到异常或者函数结束,路径回退,对于比较复杂的mkdir -p的原子性保证,也按照自己所设计的算法进行模拟检测,只有检测成功没有发生异常才会去尝试创建目录,从而保证原子性)。最后第二阶段指导书的多次细节修改也正是得力于架构优势而使得修改比较方便。
4、进度、质量和沟通管理实践体会
总结结对过程中的进度、质量和沟通管理实践体会,并分析哪些bug是因为两个人的理解不一致而导致
首先是进度上的管理。按照书上2.3节所介绍的PSP流程,我们大体上也是按照该流程来进行项目推进的,具体时间如下:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30+10 | 30+10 |
.Estimate | 估计这个任务需要多少时间 | 30+10 | 30+10 |
Development | 开发 | 1380+1800 | 1110+1690 |
.Analysis | 需求分析 (包括学习新技术) | 60+60 | 30+10 |
.Design Spec | 生成设计文档 | 120+90 | 60+60 |
.Design Review | 设计复审 (和同事审核设计文档) | 30+90 | 120+60 |
.Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60+30 | 30+30 |
.Design | 具体设计 | 60+120 | 60+150 |
.Coding | 具体编码 | 480+720 | 600+760 |
.Code Review | 代码复审 | 90+90 | 30+120 |
.Test | 测试(自我测试,修改代码,提交修改) | 480+600 | 180+500 |
Reporting | 报告 | 110+110 | 55+85 |
.Test Report | 测试报告 | 60+60 | 30+60 |
.Size Measurement | 计算工作量 | 30+30 | 5+5 |
.Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20+20 | 20+20 |
合计 | 1520+1920 | 1195+1785 |
对于PSP实践上,我们尽可能的合理估计每一步所需要的时间,而在具体实践上,难免会有一些部分错误估计了。在需求分析、和生成设计文档上,我们的更多是在互相讨论之间得出结论,所以总体时间比预计的来得少。而真正实践的时候也发现一个问题,我们代码实现上的时间往往是高于测试时间,而我们自己理论上所预计的时间二者应该是近似相等的。我们最后复盘的时候发现,其实对于测试的部分,我们在编写代码的时候就会开始着手记录测试要点,对某些分析时候的细节进行记录,并思考相对应的测试用例,从而达到比较有针对的单元测试。所以单元测试更多的是压力测试。不过有点遗憾的就是,以上每一个部分理应是连贯的,而我们因为时间离散,从而很多部分都是通过多个小段时间拼接起来,所以并没有达到理想的效能。
而质量上管理,可以说是二者达成的共识。我们最终还是确定以wcf为整体架构,同时规定对应的命名规范,以及对应的注释编写(即javadoc的编写,和关键步骤的注释)。有意识的去封装某些可以复用的函数,并着重标记有用的函数逻辑(如链接的重定向问题)。在编写的时候就由另外一名同学进行复审,再进行一次单元测试,从而确保逻辑正确。良好的代码管理,使得测试发现bug时也能快速定位位置,即使测试人员有些忘记代码逻辑,也能通过所写的注释迅速明白,从而提高效率。
在沟通管理上,问题还是比较多的,首先就是沟通难免会遇到冲突,冲突之后就需要双方能够清晰地表达自己的意思。而本次编程,我们尽量采取一起结对编程,即写代码的时候一个人写一个看,确保在沟通充分的前提下,才开始进行代码书写,所以基本上也就是在两个人理解一致的前提下才进行代码编写,所以整体上不存在因为代码理解不一致而产生的BUG,而所出的BUG往往是两个人同时理解错而产生的。
建议环节
提出建议:根据三个阶段的结对项目的实践经验,对如何更好的实施和管理结对项目提出自己的建议
- 了解对方能力所在,明确扮演角色
我认为,首先整个结对项目不能直接分隔开,以第二阶段为例,不可能两个人各自去完成一个系统。而是对应的去了解对方,我认为,代码编写如果二者风格能力差距较大还是主要以比较优秀的那位为主导,如果队友思绪敏捷,想法比较多,可以考虑做单元测试。但是回到结对编程,二者还是需要另外一位队友渗透其中的,但是大体上所扮演的角色还是得明确的。
- 进行充足的交流
这一点的体会还是比较深的。我们尽管保持在交流过后才进行代码编写,但难免还是存在一些疏漏。所以这里我们也总结了几点:首先,交流规范。对代码架构规范,和命名规范达到二者的共识,从而方便双方进行代码复用、代码复审、单元测试和BUG修复。其次,交流需求分析,二者需要统一了解整体的需求,相互补充。再者,交流设计,具体编码可能不同,但是大体的设计思路必须要明确,尤其是第二阶段这样涉及多核异常抛出的情况更需要明确好思路。最后,交流细节,细节不能整体设计的时候讨论,这样提取陷入焦虑,反而在整体有大概理解之后,再去交流细节,包括实现细节(需要如何组织数据结构),易错点分析等。
- 时间连续,决策果断
其实在体验结对的时候,我们也遇到了比较纠结的问题而进入停滞,停滞的结果有时候就会采取提前结束结对的方式,这样子会打断节奏而提高效率,所以遇到纠结的问题,我想还是得果断一点,要么在issue区进行提问,要么进行下一步,预留空间。
CI体验感想
通过这次结对编程,你对 CI 的使用体验如何?你对这一工具有何认识?
作为第一次体验CI测试的小白而言,这次的CI体验,给自己丰富了知识(涨知识了啦)。CI工具通过向.yml文件编写命令行指令,通过某些特殊语法,来实现自动化测试,在每一次push时触发,可以说是一种方便的测试方式,也是一种很好的代码管理途径。
就这次CI体验来说,首先学习了用Maven来管理代码,实现代码的工程化管理。并联动JUnit单元测试和cobertura计算覆盖率。虽然从零开始学习这些东西比较陌生,甚至一开始还走了许多弯路,但是经过三阶段的使用,不得不说CI持续集成确实带来了很多便利的地方。当然我们接触的还算比较少的,毕竟只是自己去学习有关.yml的相关,还未涉及到Runner的编写,后者又拥有许多可以深究的地方。
结对编程感想
1、结对编程方法与经历分享
描述你们结对的方法、结对过程中遇到的困难与收获,结合自己的结对经历,说明结对编程的优点和缺点,分享可以推广的结对妙招。
- 结对方法
我们主要采取的是线下结对的方式。同时并未过多的按照书上所说的“领航员与驾驶员频繁互换身份的方式”,而是稳定地让一让驾驶,一人领航。遇到的困难,在克服过后回顾起来也不算什么困难了,反而变成了收获:时间难以协调——提高时间利用率;线上交流难以表述清楚——总结问题,到下一次见面时一齐解决;交流产生矛盾——提高交流能力。结对经历总体而言还是相当不错的。
- 优缺点分析
优缺点可以具体参考我们的前两篇博客:
总的来说:
优点:效率提高、促进规范、促进交流、开发稳健
缺点:需要磨合、协调时间、冲突
- 妙招分析
其实分享的也就是我们认为自己做的比较好的几个点:良好的代码规范促进双方都能很好理解代码;低耦合高内聚的架构更有利于代码优化和BUG修复以及迭代开发;劳逸结合,这个真的很有必要,偶尔和队友一起放松一下也不错。
2、队友评价:
评价你的队友,使用汉堡点评法评价你的结对伙伴,务必给TA 提改进意见
cwm
wcf同学的代码风格良好,这次经历中我学到了如何更好地书写代码。在书写代码时,他会考虑代码的可扩展性和可读性。在第一次软工作业时,我想要下手写代码,但是他却拦住了我然后一丝不苟地开始给每个函数写TODO,然后把函数中可能会被改变的所有变量都在注释显眼的地方都标注出来。
通过这次结对编程的经历,我从他身上学到了很多,如果我能够更早意识到要记得标记清楚所有全局变量的修改处,更早地意识到梳理代码逻辑是如此的重要,也许当年编译就不会出那么多Bug了。此外,我还从他身上看到了什么叫做勤奋。当我在写代码的时候,他也在写;当我不在写代码的时候,他还在写代码。大家都很忙,可是他如同一个永不停息的陀螺一般,总能够在繁忙的日程表中挤出时间来完成软工。最后,我还看到了他对于完美的不懈追求和努力。当我过了弱测就想摸鱼时,是他一直在身边叮嘱我继续测试,继续构造样例,提醒我不要因为过了弱测就放弃对代码的测试。从他的身上,我看到了他对待软工、对待学习的态度,看到了他对于自己作品负责任的态度——他不会允许自己的代码错漏百出。总而言之,这次和他一起结对编程我获益颇丰,非常感激能够有这次机会一起和他交流学习。
wcf
这次结对编程使我认识了一个贴心、细致、周全的男生。
他非常贴心。在一起结对的几个深夜里,每当我口渴时,手边总是有一瓶刚买回来的矿泉水;每当我嘴馋时,身边总是有着不多但是却足够我解馋的小零食。当我已经头脑发胀、写不下去时,有人陪我一起下五子棋、玩24点;当我在深夜的寒风中微微发颤时,有人为我默默地披上外套。虽然我不是一个从小缺爱的小公主,但是谁不希望被好好对待呢?又有谁能够拒绝队友递过来的水、拿过来的外套呢?
他还非常细致。面对一万多字的指导书,有时真的很难把细节都给考虑到,但是cwm却能够在审阅完代码以后,对着指导书细致地一遍又一遍复查,最后指出我没有考虑周全的小细节。
此外,他还是一个非常合格的领航员。在我看来,船要在大海中航行,不仅需要充足的动力,还需要合适的舵手和称职的船长。指导书刚一发布,他就制定了时间表,安排好了我们两人各自的分工,当某个人的进度明显慢于预期时,他也能够及时而委婉地推进进度。作为领航员,他很好地把控了我们的节奏,让我们不疾不徐地推进项目,让我们在没有做完时心里有底。可以说,正是他的担当与付出,让我在这次结对中感到舒适,我只需要做好自己的本职工作就能够取得较为理想的结果。我非常庆幸能跟这样的队友一起搭档。
3、使用工具
描述在本次结对编程的过程中,你们使用了哪些软件工具,是如何应用于实践的
交流地点:宿舍、13公寓B1洗衣房、新北区食堂B1,2F,4号教学楼308,微信进行线上交流
代码编写:使用IntelliJ IDEA软件,利用maven项目进行代码管理
单元测试:JUnit4
项目部署:gitlab-ci持续集成
代码版本同步:为了减少无效提交,特意创建了github仓库来管理,以及比对修改
4、感悟和体会(吐槽)
描述通过本次结对编程的感悟和体会,对本次作业的有哪些想吐槽的,觉得本次结对作业内容可以在哪些方面做出改进?
总体来说,体验上,结对编程这几天确实非常累,我们队友都在吐槽着结对作业占用了自己太多的时间,尤其是我们都还有其他很重要的事要做的的时候。但是最后真的渡过的时候,我们都发现了,结对编程也让我们收获了挺多,不论是编程上的还是协作上的。
对于课程组,首先我们非常感谢助教们辛勤的付出吗,但还是希望指导书说明较为模糊的地方越少越好,同时宁可复制粘贴部分文字也请不要写“下文中不再赘述”,因为我们人毕竟不是机器,写“不再赘述”就意味着我们在写代码时必须要来回看和检查,想必助教们在出评测用例的时候也来回翻动了好多次吧……如果指导书能写的清晰明确,就能够省去大家许多不必要的劳动,为大家节约时间。