2021s软件工程——结对项目第二阶段

2021春季软件工程(罗杰 任健)

项目地址

1020 1169

1 结对感受

这一次的作业比上一次难了好多。

主要表现在:

  • 指令复杂:

    以softLink(), hardLink()为首的命令涉及的情况很多。

  • 指令间耦合度高:

    比如创建了一个链接,那么在移动或者复制的时候,就不仅要考虑到mv, cp等指令,也需要考虑到软链接、硬链接本身的特性。

  • 异常情况复杂:

    异常不仅种类多,抛出的情况也多,选择哪一个抛也是重点。

  • 不只是文件系统,还涉及到用户系统:

    两个系统的交互也是很重要的设计的点。

 

在软件的不同阶段,我们遇到了很多不同的问题

设计阶段

因为本身这次作业很难,所以我们在程序的构思上占用了不少比预订情况要多的时间,这其实从侧面暴露出来我们对于问题的理解不够深刻,对于自身的估计也不到位。

我们一开始被复杂的软硬链接吓到了,完全不知道该怎样下手。看了看issue区的讨论发现大家对于链接的问题竟是这样面面俱到,以至于我们有一种感觉——我们不管怎么写,都可能是错误的。

后来我们进行反思,发现这是我们设计过程中的很大的缺陷。我们没有先去思考软硬链接的大体实现,而是不断去思考这些边边角角的东西,导致莫名其妙的问题不断出现。像盲人摸象一样,一会摸出来个鼻子,一会摸出来条腿,一会摸出一根象牙,一会摸出一条尾巴......摸出来的东西越来越多,而我们对于整头大象的认识却越来越远。

这给我们的反思,就是我们遇到一个复杂的问题,应该从整个系统的角度去考虑,要考虑到不同事情之间是相互联系的,而联系则是源于我们的对于整体框架性的认知。

把指导书分析好。指导书虽然是一条一条地排列的,但是每一条要求之间都是有联系的。

仔细分析一下,无非就是“src、dst、Exception、操作之间"处理,然后每一个元素之间可能会有这样那样的状态。把握好每个指令中,每一条要求之间的联系,事情似乎就不是那样复杂。

编码阶段

编码阶段,我们总结了之前的编程经验,不是黏在一起限制思路,而是处于“编码、交流、总结”的循环之中,通过微信等方式进行交流。

这样做的好处,是给了每一个人更长的时间、更好的环境进行思考,同时交流的时候,也能更加简明扼要地抓住主题思路,从而提高了不少的效率。同时,避免了上一次结对编程中,因为总是对细枝末节的

但是正如钢镚具有正反面那样,我们采取的改进方式不可能完全都是优点。遇到的问题,总结起来,有如下几点:

  1. 压力变小,动力也变小。

    不管是否承认,没有了面对面的压力之后,尽管我们还是在线组队编程,但是对于代码的谨慎的态度、不敢划水的心理等等在第一次结对编程中的优良品质,丢了不少。

  2. 代码复审的难度加大。

    我们采取的阶段性的“编码、交流、总结”的方式。增加了交流的间隔,导致了代码复审的时候,思维跨越比较大,需要不断地提问、解答,才能明白代码做了什么。降低了不少的效率。

 

的确,一开始我们也思考,这样的方式进行编程,是不是违背了结对编程的原则呢?但是后来认为,所谓软件工程,就是在不断做取舍的过程,就是在具体问题具体分析的过程,教条化不是好事。所以我们进行这种尝试总体来说是有益的。

测试阶段

我主要负责的是测试阶段(才不是写代码的水平太次,轮不到我写代码),在测试阶段我们发现了很多问题,也总结了很多有益的经验。

  1. 测试很重要。 我们认识到了软件工程中,随着代码量的增加,程序复杂度也越来越高,单元测试、回归测试等工程领域上运用很多的测试方法变得越来越重要。 很多bug在单纯地编码过程中并不是那样的好发现。但是结合单元测试,甚至利用极限编程思想中的“测试驱动开发”对于代码的鲁棒性、正确性测试是很有用的。 我们就通过仔细地设计单元测试方法,在诸如根目录等边界情况进行反复测试,同时注意异常是否正确抛出,实现了注意每一个指令的单独测试。然后将不同的指令进行组合,注意指令组合间的复杂的传递性,发现很多问题在单个指令中是不可见的,但是在多个指令依次进行的情况下却不同。

  • 测试逻辑很重要: 测试要做到全面、不留疏漏,一个很重要的做法,就是自顶向上,从需求分析,从整体分析,然后逐步深入到代码层中去。 了解需求的内在逻辑很重要,先创造一个如下的通用的文件结构,可以通过列表等方式,把可能存在的问题一一列举,分门别类,再对于不同类别设计不同的测试方法。 这样的从整体出发的的测试方法,不容易出现大方向上的漏洞,同时随着开发人员数量的增加,这样的测试方法会更不容易出现漏洞。 但是,我们也发现,我们一开始进行测试的时候却不是这样的。那种测试法,我们总结时候将其称之为“灵光一闪测试法”、“想到哪里测到哪里测试法”......这样的测试方法具有一定的好处,那就是很多不容易发现的漏洞,通过灵感就发现出来了。但是随着软件越来越复杂,靠着这种朴素的、直观的方式去发现漏洞变得越来越不可靠,必须要有方法论来知道漏洞的测试。

  • 测试时机很重要:

    我们的开发方式,事后进行总结,其实是瀑布开发模型。我们预想的是,先设计的很完善,再编码的很完美,再测试的很通透。但是理想很完美,现实很骨感。最大的问题就是,随着指导书的不断改动,我们也需要对设计不断变动,导致我们的设计越拖越长,压缩了后面的编码时间和测试时间(连这篇博客都是在火车上写的)。 而且,这样的开发模式,对于我个人来说,最大的问题处于测试阶段。我面对浩如烟海的代码,完全不知道该从哪里进行测试。代码写的比较复杂,白箱测试不容易;黑箱测试也找不到相应的问题。就陷入了时间的黑洞——出力不出活。笔者通宵了一个晚上,把文件系统测试了四分之一不到,剩下的测试只是草草覆盖了事。

    这样的经历让我深刻地理解到,老师上课强调的“先写最小可用版本”、“测试和开发同时进行”石油多么的重要。

    总结

  1. 整体性、系统性思考很重要。

  2. 从最小可用版本出发,不断测试迭代开发,是处理复杂问题的好方法。

 

2 设计与实现思路

MyUserSystem

增加GroupUserMyUserSystem

便于可能的用户、用户组的权限扩展

由于是多对多关系,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方法,Directorycopy方法递归实现

 

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
posted on 2021-04-02 21:30  BuniQ  阅读(111)  评论(2编辑  收藏  举报