【老物】初见,结对编程!(上)

项目 内容
这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健)
这个作业的要求在哪里 结对项目-第一阶段
我们在这个课程的目标是 和团队开发真正的软件,一起提升开发与合作的能力
这个作业在哪个具体方面帮助我们实现目标 通过结对编程学习协作设计与编码、代码复审、CI使用等
成员介绍
项目 内容
结对项目第一阶段的Gitlab仓库地址 Pair Programming
二人的学号后四位
博客地址 MadokaHomura(朱正阳), zixfy(赵子轩)

一、关于解题


1. 设计与编码

a. 事前设计

本次作业最终需要实现一个基于内存的文件系统,它允许用户通过各类输入指令来进行文件的增删查改等交互,数据结构是树型结构。

在逻辑上某一条指令的处理流程图如下:

graph LR
input[指令输入]-->parse[解析地址]
parse.->pathE>路径格式错误]
parse-->instrType{指令类型}
instrType--增-->instrAdd[获取路径倒数第二级目录]
instrType--查删改-->instrOther[获取路径最后一级目录/文件]
instrType--递归创建-->instrMkdirp[递归创建目录]
instrAdd.->getFileE>目录/文件不存在]
instrOther.->getFileE>目录/文件不存在]
instrMkdirp.->typeE>目录/文件类型冲突]

instrAdd.->typeE>目录/文件类型冲突]

instrOther.->typeE>目录/文件类型冲突]
instrAdd-->addOp[增加子目录/文件]
addOp.->existE>子目录已存在]
instrOther--查改-->modifyOp[操作文件/目录]
modifyOp.->contentE>文件内容格式错误]
instrOther--删-->delOp[父目录删除此目录/文件]
delOp.->delPerE>删除工作目录或其上级目录]
style pathE fill:red,fill-opacity:0.1
style contentE fill:red,fill-opacity:0.1

style getFileE fill:red,fill-opacity:0.1
style typeE fill:red,fill-opacity:0.1
style existE fill:red,fill-opacity:0.1
style delPerE fill:red,fill-opacity:0.1

因此我们将程序所需实现的功能/模块列出

  1. PathManager: 一个解析路径的迭代器
    a. 通过路径构造,并在构造时判断路径类型(绝对路径、相对路径)
    b. 将路径分级(分段),作为迭代器返回下一级解析出的单级地址

  2. FileLike: 文件类/目录类的父类,下简称类文件

    a. 保存类文件的基本信息, 名字,绝对路径,创建/修改时间,父目录,大小
    b. 自底向上更新文件/目录大小

  3. Directory: 目录类,管理子文件/目录
    a. 对子文件/目录进行增、删、查
    b. 向文件系统提供根据路径索引文件/目录的静态方法
    c. 向文件系统提供根据路径索引对应文件/目录的父目录的静态方法
    d. 向文件系统提供根据路径递归创建目录的静态方法

  4. File: 文件类,管理文件存储内容
    a. 追加/覆写文件内容
    b. 对文件内容进行转义以输出

  5. MyFileSystem: 文件系统类,通过调用底层类按指令语义进行操作或抛出异常

我们随手画的草稿如下:

最终经过缝缝补补改改删删后完成时的UML类图

而且一条指令的执行过程中可能在流程图中任一执行阶段触发异常,因此我们使用Java的异常处理机制来处理错误输入,我们所定义的各种异常类如下

由于本次作业不区分不同异常输入对应的输出信息格式,因此只要在MyFileSystem中的方法调用过程中catch到相应异常就直接输出Path x is invaild的错误信息,如果后续需要分辨不同的异常情况,我们只需修改嵌套的try-catch-throw的逻辑即可

b. 具体细节

对于PathManager迭代器的实现,选择了使用自动机,因为路径的格式定义比较简单,所以用自动机编写不难,而且可以清楚地考虑到各种特殊情况。并且迭代器分级解析地址时同时进行了格式检查,所以相比于check + split不需要额外存储空间

对于*"根据路径索引对应文件/目录的父目录”*, 考虑到我们规定PathManager解析完时next()返回特殊值null,因此只要将目录分成根目录、单级目录、两级以上的目录(/, /a, /a/b or a/b/c/......)三种情况分别处理就行了,同时在获取父目录结束时解析出最后一级地址(文件名),Java不能返回多值,因此封装了一个数据结构保证只扫一遍地址就okay

对于文件内容的转义,我们的实现是在文件类File中只维护转义后的文件内容,在fappend指令中对连接处可能新产生的"@n"作特判

c. 测试与提交

mytql, 根据他的方法我们在CI / CD运行环境中配置好了maven,并使用jacoco-maven-plugin生成测试结果报告(一个网页和一个表格),最后一次提交得到的测试报告如下图

cobeturajacoco都能得到这种页面,但在查看分支覆盖率时貌似都只能查看某一分支的遗漏分支数量,并不能了解在条件表达式在何种布尔表达式下是没有被测试过的,希望能有dalao指点(

为了代码覆盖率达标,当然要对各模块中的异常的抛出也进行测试啦,我们使用Junit中的函数标注@Test(expected = myException.class)进行抛出异常的断言,以及在测试函数中自己手动进行try-catch测试异常是否如预期抛出

二、关于结对


1.结对形式

现场结对编程的照片:

专注中...
(摄于2021年3月23日)

2.结对流水账

  • 3/22: 摸石过河
    • 15:05 - 15:30: 设计与思路交流
    • 15:30 - 17:30: 编码
    • 18:21 - 20:20: 编码
    • 20:40 - 22:30: 编码
  • 3/23: 拨云见日
    • 15:55 - 18:25: 编码
    • 18:30 - 20:30: 编码
    • 20:51 - 21:20: 总代码复审
  • 3/24: 再复审
    • 14:31 - 16:20: 代码复审 评测 报告

在本阶段二人决定角色定位将更有倾向性,但是二人进行了充分的交流,并在一些时间段交换工作,所以都对代码知根知底,两人不同角色的工作比例大概都是七三开。本次朱正阳担任Driver,主要职责是具体源代码实现,赵子轩担任Navigator,主要职责是测试与把握整体设计

3.结对感受

(a) 朱正阳

在第一次结对编程中,我主要担当的是驾驶员(Driver)的角色,通过这次结对编程,我主要从以下两方面发表自己的感想

  • 关于结对编程

    最开始结对的时候效率并不高,我认为原因主要有以下三点

    • 两个人在某些细节上想法有所不同
    • 交流上存在一定障碍,有时双方都无法及时get到对方的点
    • 我没有提前做好准备,许多东西我还需要进一步消化理解

    随着结对不断进行,双方交流效率也提高了,任务效率也逐渐高了起来。

    结对编程也是一个很好的向他人请教的机会,能够近距离看到队友是如何编程的,对于这个问题他又是怎么想的,确实能够收获很多。

    结对编程还是一个强迫与他人进行思想上的交流以及传递自己想法与观点的过程。

  • 关于开发流程

    在此前的编程中,我很少将大部分精力放在设计以及测试上。通过这次结对,我学习到了很多关于单元测试的知识,也真正认识到了设计以及测试的重要性。

  • 关于队友

    有一个非常靠谱的队友确实可以大幅提高编程的效率以及质量。队友的编程习惯比较好,对于程序的架构也把握的很好;在我编程的过程中不断的指导我,耐心纠正我的很多不良的编程习惯以及冗余;做事比较认真负责。通过这三天的结对编程,我也从他那里学习到了很多东西,这是我一个人编程所做不到的。我很感激能有一个这样的队友。因此后续也会一起努力,一起进步

接下来应该还会有第二次第三次结对编程的任务,希望能在之后的任务中继续向队友学习,更加放开交流,努力完成任务,提升自己。

(b) zzx

在第一次结对编程中,我主要担当的是Navigator,我的体会与反思:

纸上得来终觉浅,在亲历Pair之后,我认为Pair最大的好处是提升代码的质量,毕竟在二人协作时读懂代码是交流的先决,总是会不自觉地想去保证代码的可维护性。我认为好的结对编程需要以下长期的要素: 信任、执行力以及可见的详尽计划

  1. 关于信任,我想这就是教材里说的道德水平吧,但这更应该是每一个程序员的基本素养。单枪匹马在以后工作中更是不可能,所以无论是采取何种的合作形式都应该对搭档给予信任,这是“平等地位”的先决,本次我与zzy全程都在相互支持,并及时进行沟通
  2. 关于执行力,这是Pair效率的保证,好在zzy与我都是做事比较投入的那种人,结对现场基本没有摸鱼机会(。但实际上也因此没有保证每小时都有休息时间,以后一定会协调好时间的
  3. 关于计划,这是我们本次有所忽略的,我作为Navigator没有给出细致到小时级别、功能级别的编码计划,这一方面使得编码变成了步步为营,另一方面也导致有时二人并没有是为了同一个小目标而工作,从而降低了工作效率。我决定后续一定要在动手前先制定好工作计划,磨刀不误砍柴工

我在本次Pair中发现自己的不足如下:

  1. 上文3.
  2. 我向来是不擅长与他人深入交流的baka,因此Pair实际上是对我的一次考验,而且我在经常性的交流中更难专注想事情
  3. 我太菜了,作为编程苦手,想到要6天内完成OO的“第五单元”就顿感亚历山大。在担任Navigator时不能保证总是给zzy正确有效的指引,对于整体架构,在实现时自己却还在改来改去。在自己写的一些方法里还Bug频出
  4. 本次Pair之前我对Junit一知半解,但为了能跟上整体代码的进度,所写的单元测试代码便相当的臭长

总之第一阶段的Pair让我明白了团队计划与编码能力的重要性,希望后续能努力做得更好

三、工作量

1.PSP表格

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

2.码量

Java 总行数 代码行数 注释行数 空行数
Src 918 629 180 109
Test 745 622 0 123
Total 1663 1251 180 232