终——提问回顾与个人总结
项目 | 内容 |
---|---|
北航2020软工 | 班级博客 |
作业要求 | 提问回顾与个人总结 |
我的课程目标 | 学习软件工程,掌握团队合作,锻炼自我 |
个人博客 | 提问博客链接 |
问题解答与分析
问题1 单元测试是否并无必要,而应该转为使用自动评测机暴力测评呢?或者说用单元测试为辅,暴力测评大面积覆盖为主?
最初,我根据OO作业的思路,认为软工也依然是使用自动评测机暴力测评,但是经过个人项目、结对项目、团队项目之后,确实有了一些改观。
我认为,单元测试和暴力测评是相辅相成的,尤其是在一个功能逐渐完善的软件项目中。我们的个人项目是实现求交点的任务,这时里面只需要考虑直线和圆。那么使用最简单的公式就可以求出两者的交点数,所以我为了检测正确性,依然使用了大规模覆盖进行正确性测试与速度测试。可见,对于这种较为简单的、不需要特别多扩展的项目来说,暴力测评依然是比较好用的一种手段。
可是到了结对项目的部分,我才发现暴力测评只能对于大部分情况有敏感性,对于小部分边界数据实际上是很难测出的。况且此次代码还需要加入GUI的元素,这就更无法使用暴力测评来达到应有的效果了。首先说计算部分,这次加入了射线与线段,这也就导致了除了切线,我们还需要考虑端点等边界情况。而使用了自己的暴力评测之后,发现是全对的,但是通过构造数据才发现某些端点因为精度问题是无法识别的。于是,我们进行了详细的单元测试,对每一处功能进行了覆盖。果不其然,在这种复杂问题的背景下,暴力测试有部分极端但很重要的情况是无法识别的。同样的,对于GUI部分,是无法通过暴力测评来进行正确性检验的,我们将模块拆解同时进行单元测评,进而完善了各种功能。
最后是团队项目,这是一个比结对项目难的多的工程,基础的代码也是微软公司的代码,同时涉及到了服务器、网页等。所以我们是无法使用暴力评测的,只能通过Pylint等工具,首先进行格式检测,再根据划分出的模块进行单元测试。
所以我现在认为,对于简单的、不需要进行扩展的项目,可以为了省时间而采取大范围暴力测评;对于困难的、扩展性强的项目来说,则必须引入单元测试,同时在单元测试后可以使用暴力测试进行检验,以保证所写代码的正确性。
问题2 结对编程似乎并无必要?
我的结论是,结对编程是比较重要的一种合作形式。
首先需要说起结对项目,这是需要计算直线、线段、射线、圆之间交点的一个项目,同时需要支持GUI,给的时间也比较短。这个任务看似很难,甚至需要从头学习某些技能或者知识,但是如果两个人分工合作,比如某个人负责计算部分,另一个人负责GUI部分,那么难度绝不仅仅是一分为二的减少,而是减少到多少分之一。因为如果是自己写一个这样的项目,我想会有很多人直接将GUI和计算部分耦合在一起,也没有办法想到计算优化的方法,同时工作量会呈现几何倍的增长。
由于本次是和认识的一位实力很强的同学进行合作,所以我觉得本次合作十分愉快。他负责计算部分,我负责GUI部分。虽然最初的时候完全没有什么思路,但是我们互相发动了人脉关系,和其他组的同学进行交流,互相分享信息,我们两个最终制定下了较为完善的计划。
因为是两个人写的缘故,我们的GUI和计算部分几乎没有耦合,只有数据流的传递,我想这和结对项目是分不开的。在分别测试两个代码的正确性时,我们会互换代码进行双重检查,这样更可以避免因为一个人的疏忽儿导致的整个项目崩溃。
所以我现在认为,在过去的结对或者团队实践中,我确实遇到过所谓的“猪队友”,但是现在我觉得这确实不能一概而论,只要有了靠谱的队友,我相信,结对编程是一种十分必要的形式。
问题3 在经常变换需求的情况下,我们作为代码编写者和维护者,应该怎样组织自己的代码呢?
- 可以试试原型法,降低确认和修改成本
- 写刚刚够用的代码,并经常重构
通过博客园用户的回答,我似乎有了一些明悟。
在这几次作业的实践中,结对作业的最后,有一个解耦的要求,就是需要两个不同的团队,直接互换部分模块而可以直接运行。这个要求现在我还觉得十分不合理。因为老师的要求是,要直接互换编译的部分模块,从而达到解耦合的标准。
但如果想要这样做,那么就意味着两个团队接口等功能需要完全一样,这就要求我们在做作业之前首先商量好。显然并不是一个很合理的需求。如果在工作中需要两个公司互换模块而直接能用,那岂不是需要两个公司提前把接口和思路等全部商量成一样的才可以?
本来我们以为并不需要互换的那么彻底,这也算是面对了“需求的改变”吧。
所以我们最后用一天的时间,差不多重构了我们的代码,我想大部分结对作业都会出现这样的情况吧。
我觉得,对于经常变换需求的项目,已经不是简单的组织代码可以做到的了,因为可能这个变换的需求过大,甚至需要直接推翻原来的架构才能实现新的。
对于我来说,只能希望在以后的学习和工作中,不要遇到经常变换的需求罢了,并没有想到可以回答本问题的方法。
问题4 在无法更换队友的情况下,应该如何避免“猪队友”造成的损失?又如何减少“一人带飞全队”的情况呢?
“对自己负责、对团队负责”是一种态度,会受益终身!
就好像我在问题部分阐述的一样,“猪队友”无论在任何时候都是存在的,那么如何避免其造成的损失呢?这一点我现在还无法完全解答,无论是从实践上,或是从其它资料上。
但是我比之前有了一些其他的见解。那就是,对于“猪队友”,可以尽量分配给他简单的、重复性高的工作,避免他因为要动脑子而直接“宕机”。这样虽然实力比较强的人可能会多负担一些工作,但是可以直接把一个较大的工程的拆解成大块与小块,尽可能的减小“猪队友”带来的负面影响。而且由于按劳分配原则,多工作的人通常情况下会多获得,那么只要自身的增速够快,总有一天就可以彻底摆脱“猪队友”的困扰,而可以和诸多同水平的大佬合作。
而“一人带飞全队”则又是另一种情况了,就好像本次团队作业,大家都比较负责,但是其中一位同学的能力极强,所以自然而然的,在遇到一些难以解决的问题的时候,大家会去找他请教,但也只是请教而已,而不是直接需要大佬去做。
我也同意评论中说的,要对自己负责,对团队负责。我想,如果每个人都有责任意识,或许也就不会出现“猪队友”这种称呼了吧。所以归根结底,还是需要培养自己。
问题5 对于多人合作上传后的代码版本不一致问题,是否有较好解决办法呢?
在团队合作的时候,我们各自负责不同的功能模块,但是是同一套基础代码。这也就无法避免地导致了在代码更新的过程中,出现了部分代码版本更新,而另一些还没有更新的场合。
也正像我在题目中描述的那样,这种不一致多是由于团队内配合不紧密,没有使用一种较好的维护代码的方式。比如后来我们通过使用issue进行代码协调,同时使用标准化的commit规范来尽量避免大家在提交代码时进行的误操作。
同样的,一种比较好的办法就是首先开启自己的分支,在团队的代码确认可以合并之后再进行直接合并,这样就避免了两者存在的直接冲突。
虽然提出了一些解决办法,在实践中也确实使用到了,但确实也有不太方便的地方,比如两个人实现的部分恰好有了冲突,这都是不可避免的,所以,想要完全解决这个问题,我确实还无法回答。
问题6 对于扩展代码可能导致的可读性差的问题,是否有更优雅的方式维护其功能性和可读性呢?
本问题和问题3是类似的,只不过问题3针对的是对于需求的改变,代码在需要进行变化的时候,应该怎么更好的进行修改,而本问题则是为了在增加新功能的时候,如何将代码进行扩展,看似相似,但其实有比较大的区别。
比如个人项目,需要计算直线和圆的交点,那么需要设定直线类和圆类,这样就可以对直线和圆的结构有了一个统一的规划,而不是仅仅使用数组进行协调,加属性则直接添加数组。如果没有提前考虑把直线、圆、计算等进行封装,而是使用一种松散的直接编程式的代码去写,那么在之后的结对项目中,我就无法直接通过增加射线、线段类的方法直接扩展了。
但是部分功能,我们还是进行了重构的,这当然是不可避免的一种问题。当我们的代码越来越臃肿,功能越来越多,那么势必有一天,我们最初设计的架构已经无法容纳这样庞杂的体系,这就需要我们在有组织、有结构编写代码的同时,做好重构的准备。
而在团队项目中,我还使用了pylint进行了代码的规范检查,这种方法也是一种比较有效的组织代码的方式,即根据已经较为成熟的体系来组织自己的代码,从而避免许多书写代码的坑。
问题7 应当如何培养自己的创新力?
如果没有接受教育, 会不会既僵化,又无知呢?
邹老师的评价是很中肯的,但我的意思并不是不去接受教育。
由于科研任务比较重,所以部分老师的教学课件甚至还是十年前的,这在计算机方向几乎是无法想像的。因为计算机的技术更替十分迅速,很少有可以坚持这么久的技术,那么这样没有吸收新鲜知识的教学,我个人觉得确实会导致同学们的僵化。
但如果可以跟随潮流,在学习基础知识的同时,又对计算机的前沿知识有着把控,这样我想就会更加吸引同学们的注意力,从而刺激同学们的创新力发展。
不过确实,培养自己的创新力还是需要从自己做起,而不是只靠被动的输入,应该主动去寻找知识,久而久之,才可以培养出自己的求知欲与创新力。
新问题
在解耦部分,是否需要做到两个团队直接互换编译过后的部分功能,依然可以正确运行呢?这样的要求似乎不太合理,因为每个人在设计接口的时候都要适应自己内部构造的设计。
在团队合作部分,PM如何去分配贡献分呢?由于是比较熟悉的同学,所以当PM偏心给自己分配稍多的贡献分的时候,团队内成员也无法说什么,只能拼命的内卷,这样的丛林法则是否合理呢?
对于软件工程的博客,我认为是有必要去写一些总结反思的,但是如果将其变成硬性要求,是否有些偏离初衷了呢?因为可能有的同学为了满足篇数而每一篇都不认真写,还不如去认真写一篇得到的收获大。
学到了什么
需求
需求部分主要学习到的知识点就是NABCD分析法,让我们知道了如何去量化客户的需求。这也就让团队在书写代码的时候可以适当舍弃一些繁琐但价值较低的任务,或是稍后再做一些重要但紧急性低的工作。
我认为,需求应该是之后各个阶段的基础,只有深刻的剖析了需求,才能更好的完成任务,否则一开始方向就错了,那怎么做都不会对了。
比如alpha的团队作业,我们没有完全理解邹老师的意思,所以误入歧途,又赶忙重构了一版,才能在时间要求之内交付。
设计
设计部分是需要基于需求来完成的,而设计的好坏又直接影响到需求能否圆满的完成,这两者相辅相成。设计,顾名思义,就是在工作之前首先将自己所需要完成的任务进行规划。
在团队合作中,我们首先进行了许多次探讨,分析了需求之后,我们将各个功能进行拆分并且分配到个人,尽最大程度的解耦合,从而在更新功能的时候,只修改部分代码就可以。
所以我认为,设计的部分,我所学到的最重要的知识就是需要团队成员一起探讨,提出各种合理的架构,从而将任务进行分解,之后逐个击破,大事化小,小事化了,这样才能更好的完成任务。而且正是因为我们团队进行了工作的较好设计与分配,这让我们之后的工作与验收都变得比较舒服,虽然开始我们花费了大量的时间去将任务进行拆解,但这都是值得的。
实现
以我自己所需要完成的部分来说,我觉得实现部分可能更多的是学习到了某些专门的知识。
比如对于服务器的搭建,对于python某些pdf包的应用等。这些看起来琐碎的知识点只有进行大量的学习,才能让我们的知识体系更加融汇贯通,从而有自己的风格。
同样的,我觉得对我的工作来说,仔细是最主要的部分,因为我完成的数据生成部分是需要比较多的坐标计算等硬性工作的,所以在实现过程中,我认为一丝不苟十分重要。在对接的时候,了解与自己对接的同学的需求也十分重要,由于我们做了较好的沟通,不需要修改过多代码就可以实现代码之间互联互通。
测试
测试部分在前面回答自己的问题时,我已经有了部分阐述,那就是对于暴力测试和单元测试的理解。
在学习软件工厂课之前,我一直觉得,测试就只需要设计一个数据生成器,从而进行成千上万次随机数据的输入,进而检测输出是否正确即可。
但是我发现我错了,单元测试对于这样大的团队工程来说,是完全无法避免的。因为一般来说,大的项目都需要支持扩展性,所以需要对于每一个模块的功能进行分别测试,并且保留单元测试的数据,以便在之后修改或增加某些模块的时候,不需要费心去构造其它数据。
这部分我学到的最重要的内容就是,只有将单元测试和暴力测试相结合,灵活使用,才能让我们的代码的准确性等硬性指标达到一个可以让人接受的地步
发布
发布部分,我觉得占有最大比重的应该是对于产品的推广。
这是很容易想到的点,因为发布就是为了让其他人熟知我们的项目,从而吸引反馈等,让我们进行改进。
这里就需要我们认真的去对待发布过程中像发传单一样去推广。
维护
这部分更是不可或缺,与发布相辅相成。我们对于产品的前期发布,主要是为了获取用户的使用体验,从而针对性地改进其中的某些不合理的部分,吸引更多的用户。
这里,我觉得最重要的就是对反馈回应的及时性,只有对反馈进行了及时的回应,才能让我们的产品有自己的一席之地。
理解与心得
个人项目
个人项目的题目是求直线与圆的交点。
说实话,个人项目的难度确实不是很高,因为这是我们在软工课中第一次做项目,所以也并没有特别为难我们。我只是使用了比较简单的几何公式,就可以完成本项目了。但是这里需要注意到的是,需要对代码的结构进行优化,首先是对于直线和圆进行结构化,其次是对于计算的方法也要进行可扩展话,如此一来,代码写完之后进行扩展,才会更加容易。这也是我在个人项目中学到的最重要的部分,而对于一些细节的处理,比如说浮点误差等问题,这些都是在暴力测试中发现的。
就好像我在第一次个人博客的提问中说的,单元测试究竟有什么用呢?对于第一次个人项目而言,我是没有用到单元测试的,而只是使用了暴力测试,生成了成千上万次数据自动进行测评,很好的完成了测试的需求,所以我认为,这样简单的任务只需要进行比较粗暴的测试即可,因为并不涉及到许多边界情况。
而在满足了正确性要求之后,自然要考虑速度方面的需求,本次作业的时限并不很长,但也不短,所以进行比较简单的优化就可以。比如说统计交点个数的时候,是通过set去重之后做?还是比较做?这些都是要考虑的问题。
通过个人项目,我学到了要提前规划好自己的功能,同时对于简单的任务可以直接进行暴力测试。
结对项目
结对项目的题目是在个人项目基础上衍生而来,加入了射线和线段求交点,同时加入了GUI的需求。
看似只需要给直线进行边界的设定,就可以完成计算部分的任务了。但其实不是这样的,经过仔细的分析之后,我们发现加入了射线和线段之后,代码中出现要考虑的边界情况增多了很多,幸好在个人作业中我们进行了较好的封装,从而可以在本次作业中没有过大的负担。这一切都是对于代码需求的分析与设计的功劳。
之后就是对于GUI的设计,我主要是负责这部分的。由于之前并没有学过使用C++进行编写可视化界面,所以我请教了认识的同学,发现Qt是比较合适的选择。我并没有急着直接进行代码的书写,而是先对Qt进行了熟悉,把它相关的基础功能进行了设计之后,才开始写代码,不得不说,虽然前期对于Qt的学习花费了比较多的时间,但由于已经上手,之后写代码的时间反而变得很少,很快就完成了GUI的设计,同时还支持了课程组的要求之外的功能。
我觉得两个人进行的结对任务,最重要的就是要分工明确,沟通及时。
首先,分工明确指的是要每人负责部分功能,而不至于写到最后有部分功能实现冲突,还得商量如何去修改。同时可以减小结对过程中某个人负担了大量的工作,而另一个人在摸鱼划水,这是不可取的。
其次就是沟通及时,虽然我们两个一个负责计算部分,另一个负责GUI部分,但其实对于接口的设计是需要相互讨论后成型的。而在代码实现的过程中,也是要及时沟通的,有了问题互相帮助,这样这个结对作业才能较好的完成。
与同学的合作让我知道了合作的重要性,在之后的工作中,或许会经常遇到这样需要两个人配合的情况,我通过本次的学习,也摸到了一些门道。
团队项目
团队项目的实现周期十分漫长,分为alpha和beta两个阶段,还需要写大量的工程博客,也需要和老师进行联系协商,这对于刚刚学习如何实现软件工程项目的同学们来说都是比较困难的。
我负责的部分是整个项目的主体部分,就是数据的生成。在alpha阶段,我的工作比较轻松,只需要根据传入的类型进行设计,从而生成相应数据和pdf即可。但这里是十分需要仔细的,因为我需要对文字的坐标进行计算,从而让它生成的位置更加合理。而在alpha阶段,我学到的最重要的事情不是细心,而是团队内成员的配合与沟通。
这种沟通不同于结对中的沟通,结对中的沟通是两个人进行商量,而团队合作是五人以上进行商量,每个人都可能有自己的想法,想要进行统一十分的困难,而又由于多人负责不同的部分,之间都有着比较紧密的联系,所以在完成自己部分的代码之后,需要和与自己对接的同学进行讨论,如何设计接口才能更好的让自己的模型完成想要的功能。
对于beta阶段的需求就比较困难了,因为这部分是需要我自己去对给出的文字进行拼接以及寻找位置。因为我们使用的API是一个不太完全的API,容易将本来为一段的文字识别成多段,所以在生成时就无法正确生成。这部分需要对规则进行大量的设计,所以最开始被分配了任务的时候,我是不知道如何设计一个最合理的规则的。但经过每天开会进行讨论,最终决定了一个较为合理的规则,这才让我完成了计算部分的任务。
在团队协作中,我认识到了沟通的重要性,因为单凭自己一个人去思考,对于问题是会有盲点的,我们无法做到全知全能,就只能在和伙伴思维的碰撞中得到灵感的火花。
写在最后
软件工程课就要结束了,经过一个学期的洗礼,我们终于对软件工程有了一个粗浅的认识。
无论是最初的个人项目,还是需要合作的结对项目,亦或者是一个类似于工程的团队项目,这些都让我们花费了大量的精力。但这也让我们知道了,既然选择了这条路,我们就应该好好的走下去,持续学习。
个人项目,让我知道了分析需求与设计的重要性;合作项目让我知道了沟通的重要性,我相信,这些在我未来的职业生涯中都是十分重要的财富。