草庐IT

磨练,结对编程!(中)

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

初见,结对编程!(上)

一、结对体验

1.From zzy

本次任务我主要负责test部分。随着指导书的需求增多以及异常情况的复杂,任务的难度也有很大的提升。我认为此次任务的难度主要集中在文件系统中对软/硬链接的理解,以及软/硬链接在不同指令下的表现。

由于此次作业难度的增大,我和zzx之间的讨论激烈程度也增加了。我认为这相较于上次我的单方面听从指挥有较大的进步。尽管大多数时候我的想法或观点经过讨论之后是有些问题的,但我认为能够提出问题,增进交流,也是一种进步。

虽然这次我没有当driver,但我也没能完成Navigator的职责,主要设计仍然是由zzx完成的,我只是在此基础上提出了一点建议。类与方法的具体实现我也没有参与其中。test方面做到也不够好,由于oo期间就没有认真做单元测试,我这次做测试完全是摸着上次作业的测试走路,刚开始测试效率很低。在zzx完善了测试工具类之后,测试效率才提高(甚至有时候我还写出一些错误的测试误报bug浪费时间,着实是帮倒忙)

总的来说,这次结对算是一种全新体验,虽然思路、理解、设计方面较上次有更多的参与,但是我实际编码和测试做的都不尽人意,希望下一次能够有所突破吧。

2.From zzx

  • 我对自己在这阶段的表现不满意 因为近两周课业繁重其实并没有全身心投入结对编程,为了能尽早完成编码任务没能和上阶段一样就每一部分的具体编码细节与zzy进行深入讨论。我想说下个阶段需要充分利用每一分钟了?
  • 这次新加入的ln [-s]对第一阶段的程序架构产生了冲击,原有设计无法顺利增量开发,代码量的陡增(1,611)与不少冗余的产生让我有了很重的危机感;今天我仔细地想了一下,第一阶段的一些设计暴露出了许多问题,我此刻的想法如下:
    • 每个文件/目录(逻辑上的文件)都存储本身的绝对路径是个很的设计,每条指令至多输出一条(非指令参数中的)绝对路径,更优解应该是需要输出时倒序拼接成绝对路径,进一步优化就是加入绝对路径的cache,逻辑文件被移动才可能更改绝对路径。存储绝对路径也让我在mv/cp里感到很麻烦,需要递归设置文件的新路径,逻辑文件应该只保存name/size/count/father,其他的基本信息由INode管理
    • 其次,程序在进行逻辑文件构造时要求传入父目录这个参数,我认为这也是存储绝对路径带来的衍生问题,没有父目录便确定不了绝对路径,但设置父子文件关系的功能应该下放到Directory去管理,这个地方产生了很违和的冗余
    • 第三点,对于特殊目录./..的处理,上阶段后我们将这两个目录放进了目录的子文件Map中,但这样直接混淆了特殊目录与真实的子文件,导致mv/cp在涉及特殊目录做了不少特判,但实际上只可能对特殊目录进行查询与获取,是不可能增删的,所以应该在目录类Directoryget()/contains()方法里进行特判即可
    • 第四点,我确信指导书的思想是“硬链接就是一个文件内容的另一个逻辑文件名”,所以我还是认为不应该区分RegularFileHardLink,但是我们这阶段新增的INode有问题,INode与文件内容绑定,但只有HardLink有实际的文件内容,因此有必要提取出一个FileContent类进行文件内容管理,多个HardLink之间共享的是同二个INode & FileContent
      重构计划:
    • 第五点,将所有修饰符改为public,删去无必要的异常类(现在程序有些滥用异常),删去陈冗的get()/set()以及套娃的方法控制码量
    • 当然这不会是整体的重构,只是对底层的实体类层次关系与数据结构的重构,我们会尽量保证对接口类的逻辑不产生影响,我认为本阶段程序的缺陷主要是我上阶段盲目的设计和草率的编码风格导致的。当重构不可避免时,就尽早开始吧,拖到后期只能更棘手,今晚强测后我将确认程序在整体行为逻辑上还有那些偏差,然后就开始重构吧?​

3.结对编程方式

本次结对编程采用了JetBrainsIDEv2020.2.x 起支持的Code With Me插件,相比git协作实时性更强,效果拔群总体使用的体验还是不错的,但也存在一些问题:网络连接不稳定时会出现诸如不给提示、复制粘贴失败、代码写不上等现象。本次的代码全部是以zzx为host完成的。下附工作截图

二、设计与实现

1.思路

a. 用户系统

首先是新增的用户系统类与原文件系统类之间的交互方式,本阶段我们使用了单例模式。同时考虑到AppRunner中是先实例化文件系统后实例化用户系统的,因此文件系统构造后无法获得root User的引用,因此用户系统构造后需要通知其设置root用户

public class FileSystemPluginManager {
    private static MyFileSystem fs;
    private static MyUserSystem us;
    public static void setUpFileSystem(MyFileSystem fs) {
        FileSystemPluginManager.fs = fs;
    }
    public static void setUpUserSystem(MyUserSystem us) {
        FileSystemPluginManager.us = us;
        fs.notifyUserSystemSetup();
    }
    public static void notifyUserExited() {
        fs.notifyUserExited();
    }
    ...
}

用户系统相对独立而且易实现,其大致数据结构为: UserSystem 管理全局用户表(Hashmap)与用户组表(Hashmap);UserGroup管理组内用户表(Hashmap),记录作为其主组的用户数;User管理其所属的用户组表(Hashmap);特别地,名字均不为root

b.一切皆文件

Linux中文件系统的一大核心思想是一切皆文件,文件/目录都拥有一个索引节点(iNode),其中文件iNode存储文件内容,而文件夹存储的是子文件列表。地别地,硬链接文件与一个普通文件共享一个iNode,亦或普通文件就是一个硬链接文件,而本阶段中的软链接文件可以理解为一个存储了用于重定向的路径的文件,因此我们提取出一个INode类用于存储creator, modifyTime, fileContent等实际的文件信息,而FileLike类存储AbosulatePath, size, name等逻辑上的文件信息。

文件系统中的实体关系图如下,其中FileLink是区别于目录的链接型文件,包括软链接文件(SoftLink)与硬链接文件/文件(File),注意到Directory/SoftLink所引用的INode实例并没用存储子目录列表/重定向路径,而是在本类中存储这些属性,从现实中到面向对象的转换中,这里产生了一定的冗余

关于软链接的重定向,由于上阶段中索引文件/目录分为两种方法,一种是获得最后一级文件/目录,一种是获取其父目录(考虑到mkdir/touch时其可能不存在的情况),而加入软链接后,比如touch指令中如果最后一级文件/目录是软链接,需要继续重定向,编写起来很冗余,因此整合了索引文件/目录的两种方法,保证在完全重定向的情况下保证最后一级文件/目录不是软链接,并且对于/结尾的路径,在以获取文件的意向进行索引或最后一级文件/目录确实是文件类型时直接抛出路径非法的异常

关于硬链接对INode的共享,硬链接文件与INode的链接关系在创建普通文件时隐式或通过ln显式创建。当INode文件大小修改时通知所有linked的硬链接文件进行自底向上的更新,但rm [-r]时理应递归解除所有硬链接文件和INode的链接关系,但可以这样懒更新进行优化:当文件系统中某一文件/目录被删除,其父目录被设置成null,硬链接文件自底向上更新大小时若遇到null,说明文件已被删除,此时再接触硬链接文件和INode的链接关系

2.测试

a. 第一阶段DEBUG

代码覆盖率测试固然可以十分全面地调试程序在各种可能的情况下的行为,但对于自己应该写但却没有写的代码却是无能为力的。而且做到代码测试全覆盖也无法保证程序的最终质量,我们在第一阶段中产生的Bug都是源于对极端/特殊数据的测试遗漏,这也启发我们在完成代码主体的覆盖率测试后应当更加注重程序对极端情况的处理,保障程序的鲁棒性与健壮性

b. 本阶段测试

在第一阶段过后,我们发现测试过程中有两个降低编写测试代码效率的问题:

i) 对于期望异常抛出的测试

在上阶段中异常捕获测试使用的是@Test注解与以下方法

Exception expected = null;
try {
    doSomeThing();
} catch (Exception e) {
    expected = e;
}
Assert.assertTrue(expected instanceof MyException);

为了提升代码可读性与简化编码,我们将上述方法利用泛型封装成异常测试类ExceptionTestAble

@Test
public void deleteGroup() throws UserSystemException {
    us.addUser("test");
    new ExceptionTestAble(MyUserSystemException.class) {
        @Override
        public void run() throws Exception {
            us.deleteGroup("test");
        }
    }
};

ii) 黑盒测试

在进行各模块的单元测试后进行程序整体功能的测试,但如果在单元测试函数中根据指令直接去调用FileSystem/UserSystem的函数,指令数较多时降低了测试代码的可读性,并且写起来麻烦

在尝试重定向I/O直接调用AppRunner进行输入输出捕获时发现在多个@Test方法中调用AppRunner时貌似由于其获取输入使用的是Scanner而产生了线程问题,遂弃之。我们封装了一个对拍机类用于FileSystem/UserSystem的构造,输入指令序列的解析与期望输出比对,可以使测试用例编写者只关注构造测试样例的数据本身

@Test
public void test78() {
    FileSystemTester.quickTest(
            Arrays.asList("mkdir /buaase",
                    "cd buaase",
                    "mkdir -p /buaase/dir1/dir2",
                    "ls dir1",
                    "touch /buaase/file.txt",
                    "info /buaase"),
            Arrays.asList("Make directory /buaase",
                    "/buaase",
                    "Make directory /buaase/dir1/dir2",
                    "dir2",
                    "root root 1 5 0 2 /buaase")
    );
}

由于指导书issue部分存在很多边界情况和思维盲区,我们吸取上次强测的教训,对issue中涉及到的情况也都进行了测试。

最后一次提交的测试报告如下图所示

三、工作量

1.PSP表格

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

2.码量

Java 总行数 代码行数 注释行数 空行数
Src 2,046 1,611 167 268
Test 2,052 1,770 47 235
Total 4,098 3,381 214 503

(update at 4.3)

已重构, 主要针对的是解决上文中提到的各模块功能划分不明确,底层实体关系混乱而导致的冗余与高耦合的问题,重新划分为Directory(目录)、INode(索引节点)、FileContent(文件内容)、FileLink(链接文件)、SoftLink(软链接文件)、HardLink(硬链接文件/文件)、FileLike(逻辑文件)

1.FileLike(逻辑文件)
  • 负责实现Directory/FileLink的共性方法,主要有三:getAbsPath(FileSystem)以通过文件系统获取逻辑文件的绝对路径,updateSize(int)/updateWhenMove(int)用于自底向上更新大小/递归修改更新时间,moveToDir(...)实现将逻辑文件转移到另一文件夹的功能,我们分析出cp/mv14中情况在MyFileSystem确定新文件名与新父目录后只需考虑是否覆盖与指令类型的4种情况
  • 声明copy()的抽象方法实现复制的多态
2.FileLink(链接文件)
  • 空壳抽象类,用于区分狭义的文件与目录
3.INode(索引节点)
  • 存储硬链接文件用于共享的属性
4.FileContent(文件内容)
  • 管理链接到的硬链接文件,在文件内容修改时同步各硬链接的文件大小
  • 在硬链接自底向上更新大小访问到为null的父目录时,说明已被删,解除相应链接
  • 管理文件内容的write/append
5.SoftLink(软链接文件)
  • 主要方法只有进行重定向
6.HardLink(硬链接文件/文件)
  • 通过ln建立起硬链接的两个逻辑文件共享同二个FileContentINode
7.Directory(目录)
  • 核心类,因为FileSystem所需的核心方法都在本类中以静态方法实现
  • 管理直接的子文件/目录,进行相应的增删查,在增删方法进行父子关系的建立与解除,在查方法进行./..的特判
  • 在子文件/目录管理方法的基础上封装具有语义的方法:如添加子文件夹addChildDir(),创建硬链接文件或进行修改的方法ensureChildFile
  • 核心方法detectFileLike(),通过路径索引返回一个数据结构FileDetecter,其中FileDetecter包括倒数第二级逻辑文件、倒数第一级逻辑文件、倒数第一级逻辑文件名
  • detectFileLike()的基础上为文件系统封装具有指令语义的方法,如getFileLike()/addChildDirRecursively()/getDirectory()
8. MyFileSystem
  • 接口类,完成指令语义级别的逻辑判断,调用底层Directory类的方法进行操作

重构后冗余的绝对路径与构造方法依赖父目录删去,各模块功能相对更清晰了,码量只减少了近200行,因为只是对底层类进行了重构, MyFileSystem与其他工具类基本不受影响,我们程序下一步优化的方向是针对MyFileSystemDirectory类进行优化,MyFileSystem同时进行异常的解析与指令语义的执行,有些臃肿。本阶段新增的5条指令占据了近250行,本阶段实现时已经整合了mv/cp指令,同时考虑到ln/ln -s行为逻辑相近,可以进行整合

有关磨练,结对编程!(中)的更多相关文章

  1. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  2. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  3. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  4. ruby - 如何以编程方式删除实例上的 "singleton information"以使其编码(marshal)? - 2

    我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。

  5. Ruby 元编程问题 - 2

    我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一

  6. ruby - Paperclip:以编程方式分配图像并设置其名称 - 2

    使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案

  7. ruby - 如何以编程方式检查证书是否已被吊销? - 2

    我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一

  8. ruby - 如何保持我不常用的编程语言技能 - 2

    关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭11年前。Improvethisquestion我不经常使用ruby​​-通常它加起来相当于每两个月或更长时间编写一次脚本。我的大部分编程都是使用C++进行的,这与ruby​​有很大不同。由于我与ruby​​之间的差距如此之大,我总是忘记语言的基本方面(比如解析文本文件和其他简单的东西)。我想每天练习一些基本的东西,我想知道是否有一些我可以订阅的网站,并且会向我发送当天的Ruby问题或类似的东西。有人知道这样的站点/Internet服务吗?

  9. ruby - 如何以编程方式将 mp3 转换为 itunes 可播放的 aac/m4a 文件? - 2

    我一直在寻找一种以编程方式或通过命令行将mp3转换为aac的方法,但没有成功。理想情况下,我有一段代码可以从我的Rails应用程序中调用,将mp3转换为aac。我安装了ffmpeg和libfaac,并能够使用以下命令创建aac文件:ffmpeg-itest.mp3-acodeclibfaac-ab163840dest.aac当我将输出文件的名称更改为dest.m4a时,它无法在iTunes中播放。谢谢! 最佳答案 FFmpeg提供AAC编码功能(如果您已编译它们)。如果您使用的是Windows,则可以从here获取完整的二进制文件。

  10. ruby - 以编程方式从字符串派生正则表达式 - 2

    我想输入一个字符串并返回一个可用于描述字符串结构的正则表达式。正则表达式将用于查找更多与第一个结构相同的字符串。这是故意模棱两可的,因为我肯定会漏掉SO社区中的某个人会发现的情况。请发布任何和所有可能的方法来做到这一点。 最佳答案 简单的答案(可能不是您想要的)是:返回输入字符串(正则表达式特殊字符转义)。这始终是与字符串匹配的正则表达式。如果您希望识别某些结构,则必须提供有关您希望识别的结构类型的更多信息。如果没有这些信息,问题就会以模棱两可的方式陈述,并且有许多可能的解决方案。例如,输入字符串'aba'可以描述为'阿巴''阿巴*

随机推荐