1020 1169
这一次的作业比上一次难了好多。
主要表现在:
-
指令复杂:
以softLink(), hardLink()为首的命令涉及的情况很多。
-
指令间耦合度高:
比如创建了一个链接,那么在移动或者复制的时候,就不仅要考虑到mv, cp等指令,也需要考虑到软链接、硬链接本身的特性。
-
异常情况复杂:
异常不仅种类多,抛出的情况也多,选择哪一个抛也是重点。
-
不只是文件系统,还涉及到用户系统:
两个系统的交互也是很重要的设计的点。
在软件的不同阶段,我们遇到了很多不同的问题
设计阶段
因为本身这次作业很难,所以我们在程序的构思上占用了不少比预订情况要多的时间,这其实从侧面暴露出来我们对于问题的理解不够深刻,对于自身的估计也不到位。
我们一开始被复杂的软硬链接吓到了,完全不知道该怎样下手。看了看issue区的讨论发现大家对于链接的问题竟是这样面面俱到,以至于我们有一种感觉——我们不管怎么写,都可能是错误的。
后来我们进行反思,发现这是我们设计过程中的很大的缺陷。我们没有先去思考软硬链接的大体实现,而是不断去思考这些边边角角的东西,导致莫名其妙的问题不断出现。像盲人摸象一样,一会摸出来个鼻子,一会摸出来条腿,一会摸出一根象牙,一会摸出一条尾巴......摸出来的东西越来越多,而我们对于整头大象的认识却越来越远。
这给我们的反思,就是我们遇到一个复杂的问题,应该从整个系统的角度去考虑,要考虑到不同事情之间是相互联系的,而联系则是源于我们的对于整体框架性的认知。
把指导书分析好。指导书虽然是一条一条地排列的,但是每一条要求之间都是有联系的。
仔细分析一下,无非就是“src、dst、Exception、操作之间"处理,然后每一个元素之间可能会有这样那样的状态。把握好每个指令中,每一条要求之间的联系,事情似乎就不是那样复杂。
编码阶段
编码阶段,我们总结了之前的编程经验,不是黏在一起限制思路,而是处于“编码、交流、总结”的循环之中,通过微信等方式进行交流。
这样做的好处,是给了每一个人更长的时间、更好的环境进行思考,同时交流的时候,也能更加简明扼要地抓住主题思路,从而提高了不少的效率。同时,避免了上一次结对编程中,因为总是对细枝末节的
但是正如钢镚具有正反面那样,我们采取的改进方式不可能完全都是优点。遇到的问题,总结起来,有如下几点:
-
压力变小,动力也变小。
不管是否承认,没有了面对面的压力之后,尽管我们还是在线组队编程,但是对于代码的谨慎的态度、不敢划水的心理等等在第一次结对编程中的优良品质,丢了不少。
-
代码复审的难度加大。
我们采取的阶段性的“编码、交流、总结”的方式。增加了交流的间隔,导致了代码复审的时候,思维跨越比较大,需要不断地提问、解答,才能明白代码做了什么。降低了不少的效率。
的确,一开始我们也思考,这样的方式进行编程,是不是违背了结对编程的原则呢?但是后来认为,所谓软件工程,就是在不断做取舍的过程,就是在具体问题具体分析的过程,教条化不是好事。所以我们进行这种尝试总体来说是有益的。
测试阶段
我主要负责的是测试阶段(才不是写代码的水平太次,轮不到我写代码),在测试阶段我们发现了很多问题,也总结了很多有益的经验。
-
测试很重要。 我们认识到了软件工程中,随着代码量的增加,程序复杂度也越来越高,单元测试、回归测试等工程领域上运用很多的测试方法变得越来越重要。 很多bug在单纯地编码过程中并不是那样的好发现。但是结合单元测试,甚至利用极限编程思想中的“测试驱动开发”对于代码的鲁棒性、正确性测试是很有用的。 我们就通过仔细地设计单元测试方法,在诸如根目录等边界情况进行反复测试,同时注意异常是否正确抛出,实现了注意每一个指令的单独测试。然后将不同的指令进行组合,注意指令组合间的复杂的传递性,发现很多问题在单个指令中是不可见的,但是在多个指令依次进行的情况下却不同。
-
测试逻辑很重要: 测试要做到全面、不留疏漏,一个很重要的做法,就是自顶向上,从需求分析,从整体分析,然后逐步深入到代码层中去。 了解需求的内在逻辑很重要,先创造一个如下的通用的文件结构,可以通过列表等方式,把可能存在的问题一一列举,分门别类,再对于不同类别设计不同的测试方法。 这样的从整体出发的的测试方法,不容易出现大方向上的漏洞,同时随着开发人员数量的增加,这样的测试方法会更不容易出现漏洞。 但是,我们也发现,我们一开始进行测试的时候却不是这样的。那种测试法,我们总结时候将其称之为“灵光一闪测试法”、“想到哪里测到哪里测试法”......这样的测试方法具有一定的好处,那就是很多不容易发现的漏洞,通过灵感就发现出来了。但是随着软件越来越复杂,靠着这种朴素的、直观的方式去发现漏洞变得越来越不可靠,必须要有方法论来知道漏洞的测试。
-
测试时机很重要:
我们的开发方式,事后进行总结,其实是瀑布开发模型。我们预想的是,先设计的很完善,再编码的很完美,再测试的很通透。但是理想很完美,现实很骨感。最大的问题就是,随着指导书的不断改动,我们也需要对设计不断变动,导致我们的设计越拖越长,压缩了后面的编码时间和测试时间(连这篇博客都是在火车上写的)。 而且,这样的开发模式,对于我个人来说,最大的问题处于测试阶段。我面对浩如烟海的代码,完全不知道该从哪里进行测试。代码写的比较复杂,白箱测试不容易;黑箱测试也找不到相应的问题。就陷入了时间的黑洞——出力不出活。笔者通宵了一个晚上,把文件系统测试了四分之一不到,剩下的测试只是草草覆盖了事。
这样的经历让我深刻地理解到,老师上课强调的“先写最小可用版本”、“测试和开发同时进行”石油多么的重要。
总结
-
整体性、系统性思考很重要。
-
从最小可用版本出发,不断测试迭代开发,是处理复杂问题的好方法。
2 设计与实现思路
MyUserSystem
增加Group
、User
、MyUserSystem
类
便于可能的用户、用户组的权限扩展
由于是多对多关系,User
Group
类都应该记录自己拥有的对应关系
public class MyUserSystem implements UserSystem{
private User currentUser;
private HashMap<String, User> users;
private HashMap<String, Group> groups;
void addUser(String userName) throws UserSystemException;
void deleteUser(String userName) throws UserSystemException;
void addGroup(String groupName) throws UserSystemException;
void deleteGroup(String groupName) throws UserSystemException;
void addUserToGroup(String groupName, String userName) throws UserSystemException;
String changeUser(String userName) throws UserSystemException;
String exitUser() throws UserSystemException;
String queryUser() throws UserSystemException;
}
public class Group {
private String name;
private ArrayList<User> users;
public String getName();
public getUser(String userName);
}
public class User {
private String name;
private Group group;
public Group getGroup();
public String getName();
}
MyFileSystem
扩展
package com.fileutils.specs2.models;
public interface FileSystem {
String information(String path) throws FileSystemException;
String linkSoft(String srcPath, String desPath) throws FileSystemException;
String readLink(String filePath) throws FileSystemException;
String linkHard(String srcPath, String desPath) throws FileSystemException;
void move(String srcPath, String desPath) throws FileSystemException;
void copy(String srcPath, String desPath) throws FileSystemException;
}
String linkSoft(String srcPath, String desPath) throws FileSystemException;
对于链接文件:
硬链接文件HardLinkFile
继承File
类
overrideFile
中的方法,实现与原文件同时读写
软链接,分为两种情况处理
软链接文件 继承File
类,在SymbolicLinkFile
中记录真实文件路径
重写File
的方法,实现实际操作源文件
软链接目录SymbolicLinkDirectory
继承Directory
类,便于实现目录的一系列操作
同样的,使它实际操作源目录
-
info中 软链接目录的count应该为1,不需要重定向
move
copy
方法情况较多,根据指导书分类讨论
move
方法:将一个结点移动到另一个结点下,直接移动即可
copy
方法:需要将结点完全拷贝一份,再添加到目标结点下
需要在File
Directory
中实现copy
方法,Directory
的copy
方法递归实现
3 时间记录
PSP2.1 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
Planning | 10 | 10 |
·Estimate | 10 | 10 |
Development | 880 | 980 |
·Analysis | 30 | 30 |
·Design Spec | 60 | 30 |
·Design Review | 30 | 10 |
·Coding Standard | 10 | 10 |
·Design | 30 | 120 |
·Coding | 360 | 390 |
·Code Review | 60 | 30 |
·Test | 300 | 360 |
Reporting | 120 | 120 |
·Test Report | 60 | 70 |
·Size Measurement | 30 | 20 |
·Postmortem & Process Improvement Plan | 30 | 30 |
Total | 940 | 1110 |