天天看点

初见,结对编程!(上)

项目

内容

这个作业属于哪个课程

2021春季计算机学院软件工程(罗杰 任健)

这个作业的要求在哪里

结对项目-第一阶段

我们在这个课程的目标是

和团队开发真正的软件,一起提升开发与合作的能力

这个作业在哪个具体方面帮助我们实现目标

通过结对编程学习协作设计与编码、代码复审、CI使用等

结对项目第一阶段的<code>Gitlab</code>仓库地址

Pair Programming

二人的学号后四位

3110, 3142

博客地址

MadokaHomura(朱正阳), zixfy(赵子轩)

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

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

graph LR

input[指令输入]--&gt;parse[解析地址]

parse.-&gt;pathE&gt;路径格式错误]

parse--&gt;instrType{指令类型}

instrType--增--&gt;instrAdd[获取路径倒数第二级目录]

instrType--查删改--&gt;instrOther[获取路径最后一级目录/文件]

instrType--递归创建--&gt;instrMkdirp[递归创建目录]

instrAdd.-&gt;getFileE&gt;目录/文件不存在]

instrOther.-&gt;getFileE&gt;目录/文件不存在]

instrMkdirp.-&gt;typeE&gt;目录/文件类型冲突]

instrAdd.-&gt;typeE&gt;目录/文件类型冲突]

instrOther.-&gt;typeE&gt;目录/文件类型冲突]

instrAdd--&gt;addOp[增加子目录/文件]

addOp.-&gt;existE&gt;子目录已存在]

instrOther--查改--&gt;modifyOp[操作文件/目录]

modifyOp.-&gt;contentE&gt;文件内容格式错误]

instrOther--删--&gt;delOp[父目录删除此目录/文件]

delOp.-&gt;delPerE&gt;删除工作目录或其上级目录]

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

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

PathManager: 一个解析路径的迭代器

a. 通过路径构造,并在构造时判断路径类型(绝对路径、相对路径)

b. 将路径分级(分段),作为迭代器返回下一级解析出的单级地址

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

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

b. 自底向上更新文件/目录大小

Directory: 目录类,管理子文件/目录

a. 对子文件/目录进行增、删、查

b. 向文件系统提供根据路径索引文件/目录的静态方法

c. 向文件系统提供根据路径索引对应文件/目录的父目录的静态方法

d. 向文件系统提供根据路径递归创建目录的静态方法

File: 文件类,管理文件存储内容

a. 追加/覆写文件内容

b. 对文件内容进行转义以输出

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

我们随手画的草稿如下:

初见,结对编程!(上)

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

初见,结对编程!(上)

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

初见,结对编程!(上)

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

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

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

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

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

初见,结对编程!(上)

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

初见,结对编程!(上)

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

现场结对编程的照片:

初见,结对编程!(上)

专注中...

初见,结对编程!(上)

(摄于2021年3月23日)

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: 代码复审 评测 报告

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

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

关于结对编程

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

两个人在某些细节上想法有所不同

交流上存在一定障碍,有时双方都无法及时get到对方的点

我没有提前做好准备,许多东西我还需要进一步消化理解

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

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

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

关于开发流程

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

关于队友

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

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

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

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

关于信任,我想这就是教材里说的道德水平吧,但这更应该是每一个程序员的基本素养。单枪匹马在以后工作中更是不可能,所以无论是采取何种的合作形式都应该对搭档给予信任,这是“平等地位”的先决,本次我与zzy全程都在相互支持,并及时进行沟通

关于执行力,这是<code>Pair</code>效率的保证,好在zzy与我都是做事比较投入的那种人,结对现场基本没有摸鱼机会(。但实际上也因此没有保证每小时都有休息时间,以后一定会协调好时间的

关于计划,这是我们本次有所忽略的,我作为<code>Navigator</code>没有给出细致到小时级别、功能级别的编码计划,这一方面使得编码变成了步步为营,另一方面也导致有时二人并没有是为了同一个小目标而工作,从而降低了工作效率。我决定后续一定要在动手前先制定好工作计划,磨刀不误砍柴工

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

上文3.

我向来是不擅长与他人深入交流的baka,因此<code>Pair</code>实际上是对我的一次考验,而且我在经常性的交流中更难专注想事情

我太菜了,作为编程苦手,想到要6天内完成<code>OO</code>的“第五单元”就顿感亚历山大。在担任<code>Navigator</code>时不能保证总是给zzy正确有效的指引,对于整体架构,在实现时自己却还在改来改去。在自己写的一些方法里还<code>Bug</code>频出

本次<code>Pair</code>之前我对Junit一知半解,但为了能跟上整体代码的进度,所写的单元测试代码便相当的臭长

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

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

· Estimate

· 估计这个任务需要多少时间

5

Development

开发

515

1025

· Analysis

· 需求分析 (包括学习新技术)

30

25

· Design Spec

· 生成设计文档

· Design Review

· 设计复审 (和同事审核设计文档)

15

10

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

20

· Design

· 具体设计

· Coding

· 具体编码

180

480

· Code Review

· 代码复审

60

· Test

· 测试(自我测试,修改代码,提交修改)

360

Reporting

报告

70

140

· Test Report

· 测试报告

· Size Measurement

· 计算工作量

· Postmortem &amp; Process Improvement Plan

· 事后总结, 并提出过程改进计划

合计

585

1170

Java

总行数

代码行数

注释行数

空行数

Src

918

629

109

Test

745

622

123

Total

1663

1251

232