Loading

软件工程——结对编程小结#3

项目 内容
教学班级 2021年春季软件工程(罗杰 任健)
作业要求 完成一个带用户和用户组的文件管理系统
GitLab项目地址 GitLab
结对同学学号后四位 W 同学:3812,Z 同学:3717

结对项目实践反思

问题分析

针对前面两个阶段中出现的问题,分析问题的特征、产生的根源和对质量的影响程度

代码风格冲突

在临时变量的命名上,W 同学常常喜欢使用简单命名,如 tempij 等,W 同学认为“临时变量作为中介,没有具体的含义需要深究”;而 Z 同学则力求命名准确,最好能够描述具体的操作含义,如 superDirentriesentryName 等,Z 同学认为这样命名在代码复审时能够帮助快速回想起当时的操作目的,有利于代码的复审和问题的查找。

W 同学最终选择了和 Z 同学一样的变量命名方式。在阶段一复审的时候,让 W 同学非常惊喜的是,每个方法的每一步都非常清晰,可读性很高,可以很高效地开展代码复审和问题查找:);在阶段二指导书需求复杂混乱的时候,这种清晰的命名思路帮助 W 同学和 Z 同学快速定位到了很多问题。

需求分析不明确,细节考虑不周

W 同学和 Z 同学某定程度上都算是“急性子”,没看多久指导书就开始写代码。阶段一的需求较少且较清晰,W 同学和 Z 同学很快就完成了代码实现,也顺利通过了测试;但阶段二的需求很多且不明确,W 同学和 Z 同学竟然也没看多久指导书就开始写代码了,结果一堆“不再赘述”都没有注意,“覆盖”和“视为修改”也没有准确理解,直到在讨论区看到大家的发言,才发现自己一直走在悬崖边上 ̄□ ̄||,开始从头复审代码。

W 同学和 Z 同学也算是在结对中得到了教训,开始认真阅读指导书,积极与大家交流问题。最后的一两天,竟发现了几个致命的错误。修改过后,W 同学和 Z 同学长长地舒了一口气。

单元测试不充分,没有做到代码与测试同时进行

在阶段一,W 同学和 Z 同学在编写代码的同时,也积极编写单元测试,力求对所有分支和异常都充分覆盖;但在阶段二,由于指导书需求复杂,分支条件和异常处理繁多,W 同学和 Z 同学就没有积极编写测试,而是将更多时间花在了对代码的查看和复审上,结果也使 W 同学和 Z 同学在阶段二的强测中翻了车,变量名竟然写错了Σ(゚д゚lll)。

吸取了测试的惨痛教训,相信 W 同学和 Z 同学在未来编写代码和测试的时候会更加用心ヽ(;´Д`)ノ。

需求分析实践体会

总结结对项目中的需求分析实践体会,并分析哪些 bug 是因为需求分析不足而带来的

我们并未投入很多时间进行需求分析,更多是简单看一下整体需求,然后推进到代码实现环节;在实现具体的指令时,再根据指令需求进行细致分析。在阶段一指导书需求比较明确的时候,这种做法的编码效率可能较高;但在阶段二指导书需求不明确、还伴随上下文相关的需求限制的时候,这种做法就极其不好。需求分析未从整体出发,使我们疏忽了很多细致要求,在第二阶段弱测的时候遇到了众多问题。

总结这次结对的需求分析,我们认为做得不是很好,未来可以在以下方面进行一定改进:

  • 将指导书的需求抽离出来,用具体表格或者图像的方式指明需要实现的内容,文字的表述没有表格和图像直观,并且更能理清需求的分支和脉络,而文字常常会因为两个人的理解上的不一致而产生问题;
  • 需求分析从整体实现目标出发,实现的需求有一些基本点,比如指导书中的基本声明部分,同时关注上下文的需求相关联的部分,在进行代码实现之前,需要将某一个具体实现对象的所有需求都进行明确,而不是仅仅看着它这个模块的需求。

架构设计实践体会

总结结对中的架构设计实践体会,描述通过改进设计来提高程序性能的思路和方法,并分析哪些 bug 是因为架构设计不足(特别的,需求变化)而导致

整体设计

我们的整体设计比较成功,在两个阶段的迭代开发过程中,我们没有因需求增加而对架构进行大的调整。

阶段一的指导书引入了文件和目录的概念。在目录中,既可以放入文件,也可以放入子目录;在子目录中,一样既可以放入文件,也可以放入子目录;目录的本质其实就是存储文件和子目录的容器,且形成了一种递归结构。这种结构对应到设计模式,恰好就是 Composite 模式。因此,设置 MyEntry 类作为 MyFile 类和 MyDirectory 类的抽象父类,MyEntry 类的字段有 namecreateTimemodifyTime 等。

阶段二的需求指导书引入了软连接、硬链接、用户和用户组的概念。分析可知,软链接的本质是对绝对路径的映射,硬链接的本质是对文件的映射,它们都是链接文件,均可继承阶段一中的 MyEntry 类。至于用户和用户组,只需分别建类即可。至此,我们只在阶段一的架构上新增了类、类字段和类接口,并未对其进行大的调整。

实现细节

异常处理

我们针对不同的异常场景设计了不同的异常,在进行异常测试时,能够很快地定位问题,并检查所抛异常是否符合预期。特别地,我们认为,抛出异常是文件系统和用户系统的责任,存储实际数据的文件、目录、用户、用户组,为系统提供了若干访问接口,它们在任何时候都不应抛出异常。

输入解析

我们抽离了输入解析模块,设置了 Parser 工具类,对输入的路径字符串、用户名字符串、用户组名字符串进行检查和解析。在具体的指令实现部分,我们便无需再考虑路径检查和解析问题。

Parser 中,我们专门设置了 parseFilePath(String)parseDirPath(String) 方法,区分这两个方法是为了处理以文件名结尾的路径后不能添加 / 而以目录名结尾的路径后可以添加 / 的需求。

底层方法

在阶段一中,虽然很多方法的实现框架相同,但我们并未对其进行抽离,这导致我们在阶段二的编码伊始异常痛苦。软链接的存在,使通过路径访问文件或目录不再是简单的循环。每访问到一个软链接,都要通过软链接指向的绝对路径重新访问文件或目录,这看上去像是个递归。几经商讨,我们确定了三个底层方法。这三个底层方法随后贯穿了整个项目。

/**
 * @param link 软链接
 * @return 软链接指向的路径对应的文件、目录或硬链接,
 *         如果软链接指向的路径对应的是软链接,需要递归调用
 */
MyEntry toEntry(SoftLink link);

/**
 * @param entries 路径解析后的各个条目名
 * @param path    路径
 * @return 路径对应的条目的父目录
 */
MyDirectory getSuperDir(List<String> entries, String path);
    
/**
 * @param path     路径
 * @param redirect 重定向选择
 * @return 路径对应的真实条目,可能是文件、目录或硬链接
 */
MyEntry getEntry(String path, boolean redirect);

这三个底层方法中,getSuperDir(List<String>, String) 的实现基于 toEntry(SoftLink)getEntry(String, boolean) 的实现又基于 getSuperDir(List<String>, String)toEntry(SoftLink)

在对底层方法的抽离和编写过程中,我们深深体会到了什么叫牵一发而动全身。我们将先前所有方法的实现替换为了基于底层方法的实现,同时进行测试和复审,这花费了我们数个小时时间。

性能优化

作业中的不少需求实现涉及递归,如:目录拷贝、计算目录大小。起初,我们的实现是基于递归的深度优先搜索,在压力测试中很轻松地爆栈了。出于性能考虑,我们将其替换为了基于先入先出队列的广度优先搜索。

另外,我们去除了文件或目录对于绝对路径的缓存,这在大量文件拷贝时极易引发内存溢出。

错误反思

我们在阶段二的强测中爆了两个点,这与我们的架构设计和代码实现无关,源于我们在调用构造方法时传错了参数。这个错误很低级,但阴差阳错地,我们并未发现。反思过后,我们认为是手懒的原因。实现一个 tree 指令并非难事,更何况 tree 指令可以直观清晰地展示出当前文件系统的内容,测试效果极佳。

我们在阶段三的代码中只替换了两个参数名,便通过了所有测试点。

进度、质量和沟通管理实践体会

总结结对过程中的进度、质量和沟通管理实践体会,并分析哪些 bug 是因为两个人的理解不一致而导致

本次结对编程,我们大部分时间都是面对面进行,在推进进度、管理代码质量、就指导书的理解进行沟通交流等方面,我们都开展得比较顺利。

进度和质量管理

在阶段一二的结对编程中,我们的效率都是比较高的,这依赖于我们面对面开展结对编程,且积极沟通推进效率。一个人作为驾驶员,实际编码;一个人作为领航员,提醒注意事项,关注细节实现;疲惫时,两个人交换角色。

我们在代码复审环节投入了较多的时间,梳理每个方法的运行逻辑,力求方法表达准确、条理清晰,这帮助我们找出了许多编码时由于编码结构不清晰而引入的错误。

沟通管理

在遇到理解分歧时,我们会通过手绘图表的方式及时进行交流,或者直接在 Ubuntu 18.04 上实际进行测试。对于具体的分歧,力求两个人理解达成一致;在理解一致后,与其他同学和助教积极交流,这帮助我们高效推进了编码进度。

结对建议

根据三个阶段的结对项目的实践经验,对如何更好的实施和管理结对项目提出自己的建议

结对编程是两个人的事情,在代码编写的过程中,你不仅要让自己看到代码时能准确理解代码思路,还要让同伴看到代码时也能快速理清逻辑。因此,努力提高代码的可读性,积极编写注释,准确命名变量,往往能帮助两个人在结对中更高效地配合。

遇到任何需求理解分歧或代码编写分歧时,一定要与同伴积极交流。多次阅读需求指导书,检查两个人的理解是否出现偏差,体会代码的实现方式是否存在错误隐患。把队友当做小黄鸭,是一种不错的理清思路的方式。

CI 体验感想

通过这次结对编程,你对 CI 的使用体验如何?你对这一工具有何认识?

在我们的结对项目过程中,CI 工具作为编译和测评环境,使用体验极佳,在具体操作中,我们有了以下认识:

  • CI 能够很好地进行自动化测试,只要完成一次部署,每次提交都不需要再对 yml 文件进行修改,只需要修改代码并上传,就能实现测试环境的自动搭建和单元测试的自动开展,操作简单,效率较高
  • CI 能够很好地帮助团队协作,新上传一份代码,同伴可以很方便地查看新代码对于旧单元测试的支持,以及单元测试对于代码的覆盖率

结对编程感想

结对总结

描述你们结对的方法、结对过程中遇到的困难与收获,结合自己的结对经历,说明结对编程的优点和缺点,分享可以推广的结对妙招

  • 关于结对方法,在本次结对编程中,我们主要采用的是面对面的结对方式,不论是交流沟通,还是疲惫时及时轮换,亦或是编码时两个人一起梳理代码逻辑,都能够高效进行。最重要的是,两个人可以一起吐槽指导书,分担压力,吐槽过后,再相互鼓劲,继续前进
  • 关于结对过程中遇到的困难与收获,可能就是时间比较紧张,指导书的说明不是很准确,每天都得盯着 issue 区,看看有没有新的要求变动;收获就是锻炼了应对不断变化的需求的处理能力-_-||,以及最重要的,和他人沟通,协作的能力
  • 关于结对编程的优点,主要谈谈两个方面,第一个是代码复审,两个人同时专注着代码编写的逻辑,能够减少很多低级错误,如异常抛出错误,变量名写错等等;第二个是交流沟通,一个人编写代码时很容易陷入固定思维的死循环中,而如果能够与他人交流思路与理解,能够帮助快速跳出思维定式,找到新的思路
  • 关于结对编程的缺点,主要谈谈两个方面,第一个是磨合,两个从未有过代码合作的同学,在短时间内需要适应他人的代码编写习惯,适应在他人面前编程,这有着一定难度,结对编程初期的磨合难以避免;第二个是效率,在编写代码的过程中,真真的驾驶员只有一个,在简单代码的编写部分,其实领航员并没有很大作用

结对互评

评价你的队友,使用汉堡点评法评价你的结对伙伴,务必给他提出改进意见

3812:

  • Bread:第一次与 Z 同学合作,体验十分愉快,能够从 Z 同学身上学习到很多东西,比如明确的代码命名,比如清晰的注释,比如对于代码设计的艺术和抽象,比如基于底层代码实现的效率探索,比如 commit 代码时一定写清楚做了什么修改,比如对于文字细节的注意,短短三周,感觉自己被很深刻地影响了,也开始注意很多曾经编写代码时忽视的细节
  • Meat:能够抽离的函数或者抽象的部分可以进行抽象,不需要所有的部分都想着提取公共部分,需求变更时,过高的函数内聚可能不利于代码阅读和迭代修改;更主动点分享自己的观点,更大胆地提出自己的建议,嗯,其他就没了(๑•̀ㅂ•́)و✧
  • Bread:非常优秀的队友,非常愉快的合作,在结对的三周里,我们的效率都非常高,从来没有熬夜(赞),希望有机会还能够继续合作

3717:

  • Bread:W 同学有十分广泛的技能涉猎,对新事物有很强的学习能力。在本次结对过程中,CI部署、JUnit测试、JProfiler分析、Ubuntu实操,均由 W 同学全权负责,着实令人感到靠谱
  • Meat:博客表述可以进一步规范化(吹毛求疵了
  • Bread:W 同学是非常耐心、非常细致的一个人,在结对过程中,我有时会陷入沉思,有时会感到劳累,对于 W 同学所说的话反射弧较长,这是我的问题,但 W 同学总是耐心地等我回复,并且,W 同学常常能根据指导书表述发现代码中一些遗漏的地方,超级赞!希望有机会还可以和 W 同学合作

结对工具使用

描述在本次结对编程的过程中,你们使用了哪些软件工具,是如何应用于实践的

工具 应用
IntelliJ IDEA 集成开发
JUnit 单元测试
JProfiler 性能分析,定位压力测试下程序性能开销较大的代码段,如递归调用等,进而思考优化方式
Git 版本控制,随时随地提交代码修改,且保证对修改部分做出清晰描述
Ubuntu 测试指令行为
WeChat 交流

吐槽&改进

描述通过本次结对编程的感悟和体会,对本次作业的有哪些想吐槽的,觉得本次结对作业内容可以在哪些方面做出改进

  • 关于感悟和体会就“不再赘述”了,主要谈一谈吐槽&改进的部分

  • 吐槽&改进

    • 指导书的指向不明,比如文件的移动,视作修改一次,硬链接的链接等,可以通过图或者表的形式进行描述,减少文字表述,首先文字表述本身就不够简洁,篇幅冗长,其次文字的理解可能会产生歧义,同学们大多数的 issue 也是因为没有对文字表述理解到位;

    • 通过直接交流来明确需求,改进需求方与需求实现方的交流方式,这次作业可以将助教团队看做需求方,将同学们看做需求的实现方,如果只是小部分的需求没有理解到位,那么可以通过 issue 直接反应,等待需求方明确;但是如果出现大规模的需求不明,理解偏差,其实可以安排线下或者线上的直接沟通,issue 反馈的实时性,肯定不如直接交流的实时性高,而且直接交流也能够帮助确定明确的需求,不会导致理解偏差,最后再将交流结果整理成文档,进行反馈

最后,感谢助教们在这次结对编程中的付出,看到 issue 区的助教在凌晨2-3点的时候还在进行 issue 回复,真的辛苦了!

posted @ 2021-04-09 11:19  DanGuge  阅读(95)  评论(2编辑  收藏  举报