软件工程--结对编程小结#2
结对编程小结
项目 | 内容 |
---|---|
教学班级 | 2021年春季软件工程(罗杰 任健) |
作业要求 | 完成一个带用户和用户组的文件管理系统 |
GitLab项目地址 | GitLab |
结对同学学号后四位 | 3812,3717 |
第一部分 结对编程感受
3812:
吸取了第一次结对编程的经验和教训后,这次结对编程,我们交流和协作相当顺利,编程效率明显提高。一个人编码,一个人查看指导书、梳理思路、提示细节,这样的工作方式使我们节省了很多时间,且及时发现了很多隐藏错误。
当然,这次结对编程过程中也存在一些问题,如:指导书的表述不是很明确,存在着很多模棱两可的地方。其中的以下不再赘述说明,常常使我们忘记一些细节异常的处理。本次结对编程最主要的时间花费都在理解指导书和阅读issue上,源于我们没有在编程伊始就对指导书内容进行归纳和分析,这一点我们将在下次编程时改正。
3717:
- 本次的结对编程地点选在了宿舍,外接大屏真香!
- 意识到了需求分析的重要性。由于第一次作业需求较少,实现较简单,我们并未将实现中的一些普遍操作进行封装,在本次作业中吃了不少苦头。我们花费了一下午时间来封装底层操作、重写功能实现和开展相关测试。
- 底层操作动不得!随着项目的逐渐扩大,我逐渐意识到一个很有趣的问题——所有的需求实现都依赖我们封装的几个底层操作,但凡对底层操作有一点点修改,我们的项目就炸了。曾经,我还是动不动就叫喊重构的意气少年;现在,我却感觉,项目大了就构不得了,只能打补丁(狗头。
第二部分 项目设计
2.1 架构设计
2.1.1 文件系统
文件系统沿用了先前的基于 Composite
模式的架构设计。
由于本次作业新增了软链接和硬链接,它们都是链接文件,可抽象为条目。并且,硬链接与文件在某些情况下具有相同的对外视图,故设置 FileView
接口进行适配。
类层次如下:
本次作业我们抽象出了三个底层操作,用以完成所有需求的实现:
/**
* @param link 软链接
* @return 软链接指向的路径对应的文件、目录或硬链接,
* 如果软链接指向的路径对应的是软链接,需要递归调用
*/
public MyEntry toEntry(SoftLink link);
/**
* @param entries 路径解析后的各个条目名
* @param path 路径
* @return 路径对应的条目的父目录
*/
public MyDirectory getSuperDir(List<String> entries, String path);
/**
* @param path 路径
* @param redirect 重定向选择
* @return 路径对应的真实条目,可能是文件、目录或硬链接
*/
public MyEntry getEntry(String path, boolean redirect);
2.1.2 用户系统
本次作业的用户系统需求较小,实现较为简单。(下次再说
2.1.3 系统交互
利用静态方法进行交互。
以文件系统为例:
public static void incrementCmdCount();
public static MyDirectory getRootDir();
public static MyDirectory getCurrentDir();
public static void setCurrentDir(MyDirectory dir);
public static void reset();
2.2 测试
2.2.1 回归测试和覆盖测试
回归测试,对新开发版本的项目进行先前版本的测试,保证没有因为增加新功能而出现新问题。
覆盖测试,本次测试涉及的异常种类较多,异常情况较复杂,没有做到分支和语句的完全覆盖。
在本次测试中遇到的问题:
由于文件系统和用户系统无法获取对方的实例,因此我们将文件系统和用户系统的成员变量设置为静态的,在测试时对文件系统和用户系统中的成员变量重置以开展测试。
@Before
public void resetSystem() {
MyUserSystem.reset();
MyFileSystem.reset();
}
2.2.2 压力测试
在性能测试方面,我们对文件系统中绝对路径的获取、文件大小的获取和文件的拷贝进行了优化。
在优化前,我们采用缓存的方式存储绝对路径,采用递归的方式获取文件大小和拷贝文件,如果遇到层次较深的目录嵌套,我们的程序就很容易爆栈。
@Test
public void test() {
MyFileSystem fileSystem = new MyFileSystem();
try {
StringBuilder path = new StringBuilder();
for (int i = 0; i < 2048; i++) {
path.append("a/");
}
for (int i = 0; i < 248; ++i) {
fileSystem.makeDirectoryRecursively(path.toString());
fileSystem.changeDirectory(path.toString());
}
fileSystem.fileWrite("file.txt", "2");
fileSystem.copy("/a", "/b");
} catch (Exception e) {
e.printStackTrace();
}
}
在优化后,我们取消了对于绝对路径的缓存,采用即用即求的方式获取绝对路径;我们将递归替换为层序遍历的方式,来获取文件大小和拷贝文件。
运行方面,我们没有再遇到爆栈的问题,但获取绝对路径的时间开销却大大提升。鱼和熊掌不可得兼,权衡之下,我们最终选择用时间换空间的方式对代码进行优化。
第三部分 PSP表格记录
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | 575 | 1125 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 30 |
· Design Spec | · 生成设计文档 | 15 | 15 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 30 | 40 |
· Coding | · 具体编码 | 300 | 540 |
· Code Review | · 代码复审 | 120 | 240 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 240 |
Reporting | 报告 | 50 | 50 |
· Test Report | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 640 | 1190 |