綾波レイ
AYANAMI REI

寒假作业2/2

这个作业属于哪个课程 2021软件工程实践|W班 (福州大学)
这个作业要求在哪里 寒假作业2/2
这个作业的目标 1.阅读构建之法并提问2.WordCount编程
其他参考文献 Java正则表达式 | 单元测试和性能测试

阅读构建之法并提问

PSP表格相关

PSP的预估时间与实际耗时都需要由工程师亲自填写,并且能够根据预估时间与实际耗时的不同,反映出一些问题

  • 我认为,软件开发过程中,由于BUG的存在,往往时间是最难以去确定的,这会导致预估与实际时间有偏差,难以保证两者时间相同
  • 在这次实际使用中,我感觉在编程时较为难去计算实际时间,不免会有一些碎片化时间难以去记录,而且在编程中如果在意耗时,反而降低效率,所以对于PSP表格这种衡量方式有点不理解

结对编程相关

讲义第3章b节,详细介绍了结对的编程模式。根据作者的介绍,有许多优秀的计算机领域公司的创始人均为两人,二人合作也确实造就了公司的成功,说明了结对的工作方式,确实有利于提升效率

  • 然而,实际上是现如今大部分公司均是采取团队编程的方式,很少听说结对编程的工作模式(我是在讲义中第一次听说)。较少采用结对编程的原因,讲义中并没有系统地说明
  • 我认为,可能是公司的项目工作量对于两人来说确实太大,结对的方式,虽然能提高效率质量,但是在较大项目面前,战线不可避免地比团队编程拉得长。进而我想,是否能有种折中的方案,在团队编程中,将项目分化,分化后的工作再进行结对的模式?这样的方式是否会更加有效?

团队编程相关

讲义中提到,团队成员有着不同的角色,但是都希望个人能投入工作,积极参与

  • 实际上,在大学阶段,不同个体之间的编程水平确实会有差别,我们无法强求平等。但是往往会因这种原因,导致个体的积极性不高,最终导致双方认知上的偏差,产生争吵,如何平衡是一个问题
  • 我认为,或许过于追求对于项目贡献的相当是不太妥当的。适合的人,做适合的事。尽管客观有所不同,但是只要都有付诸努力,也能提高参与感

代码规范相关

书中提及到好的代码规范,有助于阅读和理解,并且对于项目的开发同样也有好处

  • 我也是一个对于格式会有强迫的人,认可作者的观点,也是有在打代码中遵循规范。
  • 然而,要有好的代码规范,就一会一定程度上降低自己的开发效率。在学习过程中,也有时限较为紧张的时候。那么,在有时间压力的情况下,是否应该对于代码规范适当放松?

绩效管理相关

博客讲义中提到,对于软件开发人员的绩效的认与评价一直未有一个合理明确的标准。在文末提到了某互联网公司的一个业绩与价值观的评价体系

  • 讲义虽以简单、话糙理不糙的说法,解释了这一评价体系。绩效方面较好理解好与差的关系,但是价值观层面却没有给出比较准确的标准。是为人价值观方面?还是公司的价值追求与个人价值追求是否相符?

冷知识

404错误,想必大家都很熟悉了。是指用户浏览网页时,服务器因某种原因无法正常提供信息或无法回应所返回的页面,即「找不到该页面」,又被称为「互联网的最后一个页面」。

关于404说法的由来众说纷纭,其中有一种说法称,404源于「404房间」。

相传互联网的第一架服务器,架设在欧洲核研究组织的404号房。如果要打开网页,就得向404房的Berners Lee提交申请,如果他没在房间内,就会出现「404 not found」。

WordCount编程

Github项目地址

https://github.com/ArturiaXu/PersonalProject-Java

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 1.5 Days 2 Days
Development 开发
· Analysis · 需求分析(包括学习新技术) 90 120
· Design Spec · 生成设计文档 15 20
· Design Review · 设计复审 5 10
· Coding Standard · 代码规范(为目前的开发指定合适的规范) 10 10
· Design · 具体设计 30 30
· Coding · 具体编码 120 180
· Code Review · 代码复审 10 5
· Test · 测试(自我测试,修改代码,提交修改) 120 180
Reporting 报告
· Test Report · 测试报告 10 20
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 时候总结,并提出过程改进计划 20 30
Total 合计 440 615

解题思路

  1. 创建一个Lib类,在该类里面实现词频统计的各项需求
  2. 将基本的四项功能划分为四个方法,分别实现
  3. 需求涉及到字符串检验,学习并运用Java的正则表达式进行检验
  4. 需求设计词频统计,key-value形式,学习并运用map
  5. 学习并使用效率高的IO方法

代码规范

codestyle.md

接口设计与实现

接口设计

  • 分为两类,在工具类Lib中具体实现,WordCount通过调用Lib类的方法进行文件处理

  • 按需求总体分为4个主要功能函数,其余若干判断、I/O函数

  • 能根据需求不同,在handleFile()中增减功能

    private int characterNum;
    private int wordNum;
    private int lineNum;
    private final String inputFile;
    private final String outputFile;
    private Map<String, Integer> map;
    public List<Map.Entry<String, Integer>> wordsRank;
    
    /**Lib构造函数
    * @param inputFile 输入文件地址
    * @param outputFile 输出文件地址
    */
    public Lib(String inputFile, String outputFile)
    
    /**
    *调用各功能的函数
    */
    public void handleFile()
       
    /**
     * 将文件读取进字符串
     * @return 文件内容字符串
     */
    public String readFile()
        
    /**
     * 计算字符数
     * @param str 文件字符串
     */
    public void countCharacter(String str)
        
    /**
     * 计算有效行数
     * @param str 文件字符串
     */
    public void countLine(String str)
        
    /**
     * 计算单词数,并加入map
     * @param str 文件字符串
     */
    public void countWord(String str)
    
    /**
     * 单词排序
     */
    public void sortWords()
    
    /**
     * 写入输出文件
     */
    public void setOutputFile()
    

功能实现

  • 输出单词统一为小写
    • 在添加进map时,执行toLowerCase()方法
  • 统计文件的字符数

    • 不考虑中文字符,且任意ASCII码均算做一个字符

      直接返回字符串长度即可

      public void countCharacter(String str) {
          characterNum = str.length();
      }
      
  • 统计文件的单词总数

    • 单词以分隔符(非字母数字字符,空格)分割

      调用split方法,将字符串以分隔符分割

      String[] list = str.split("[^A-Za-z0-9]");
      
    • 单词以至少4个英文字母开头,跟上字母数字,不区分大小写

      将分割后字符串各自匹配单词正则

      String WordPattern = "^[A-Za-z]{4}[A-Za-z0-9]*";
      public boolean isWord(String str) {
          return Pattern.matches(WordPattern, str);
      }
      
  • 统计文件的有效行数

    • 任何包含非空白符的行,都需要统计

      调用split方法,将字符串以"\n"分割,将分割后字符串匹配有效行正则

      String LinePattern = "[\\s\\S]*\\S+[\\s\\S]*";
      String[] list = str.split("\n");
      public boolean isLine(String str) {
          return Pattern.matches(LinePattern, str);
      }
      
  • 统计文件词频

    • 按频率降序,频率相同的单词,优先输出字典序靠前的单词

      tree map默认对key进行字典序排序,无法对value进行排序

      将键值对放入List中再重写比较器Comparator,实现对value排序

      综合起来,实现不同频降序,同频按字典序升序

      private Map<String, Integer> map;
      public List<Map.Entry<String, Integer>> wordsRank;
      public void sortWords() {
          wordsRank = new ArrayList<>(map.entrySet());
          Comparator<Map.Entry<String, Integer>> comparator = 
              (o1, o2) -> o2.getValue()- o1.getValue();
          wordsRank.sort(comparator);
      }
      

性能改进

  • I/O使用用缓冲流,提高读写效率
  • 1500W字符检测,耗时27S
for (int i = 0; i < 5000000; i++) {
    writer.write("hs.(* word123 \n fqAJS \n \t%^& \n");
}

单元测试

单元测试使用JUnit5测试框架进行测试

  • 测试判断有效行

    void isLine() {
        final String test = " ";
        assertEquals(false, lib.isLine(test1));
    }
    final String test1 = " ";	//false
    final String test2 = "\t";	//false
    final String test3 = " \t";	//false
    
    final String test4 = " \t\r asad \t ";	//true
    final String test5 = "  \t    asad";	//true
    final String test6 = "asde   \t\r";		//true
    

    要求:包含任何非空白字符的行,都需要统计

    用例:1.单个空格、2.单个空白字符、3.多个空白字符、

    ​ 4.多空白字符包含可见字符、5.多个空白字符在前、6.多个空白字符在后

    考虑:1.处理时以split("\n")分割,故测试用例中没有\n字符;

    ​ 2.考虑了无效行的多种情况

    ​ 3.考虑有效行多种情况

  • 测试判断单词

    void isWord() {
    	final String test = "abc";
        assertEquals(false, lib.isWord(test1));
    }
    final String test1 = "abc";			//false
    final String test2 = "abcd";		//true
    final String test3 = "abc123";		//fasle
    final String test4 = "abcd123ab";		//true
    final String test5 = "ABcd123ac";		//true
    final String test6 = "abcd@123##$$123%^%*[]";	//false
    

    要求:至少以4个英文字母开头,后跟任意个数字母数字符号,且不分大小写

    用例:包含少于4个字母,字母与数字混合,

    ​ 大小写混合,字母数字与其他符号混合

  • 测试统计字符数

    void countCharacters() throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(inputfile));
        final String test = "Ab2$\r\n";
        for (int i = 0; i < 10; i++) {
            writer.write(test);
        }
        writer.flush();
        writer.close();
    
        lib.countCharacter(lib.readFile());
        assertEquals(test.length() * 10, lib.getCharactersNum());
    } //结果相同
    

    用例:包含了大小写字母、数字、非空白字符、空白字符

  • 测试统计有效行数(函数模式与上相同)

    final String test = "a1m23\n \n\t\n\r\t\n  \r*123&(pas\r\t\n";
    final int lineNum = 2;
    assertEquals(lineNum, lib.getLineNum()); //结果相同
    

    用例:包含了字母数字、空白字符、多个空白字符、

    ​ 多个非空白字符与多个非空字符的组合

  • 测试统计单词数(函数模式与上相同)

    final String test = "absd alskdf AsIs123\nasd\nidol][75asd\n";
    final int wordNum = 4;
    assertEquals(wordNum, lib.getWordNum()); //结果相同
    

    用例:包含了被空白字符、非字母数字符号分割的情况

  • 测试单词排序(函数模式与上相同)

    final String[] list = {
        "zaad","zaad","zaad", 
        "aaab","aaab","aaab", 
        "aaaa","aaaa","aaaa",
        "bbbb222","bbbb222",
        "dddd444", "dddd445"};
    lib.countWord(lib.readFile());
    lib.sortWords();
    //有多个对比函数,此处仅举例,其余类似
    assertEquals("aaaa", lib.wordsRank.get(0).getKey());
    assertEquals(3, lib.wordsRank.get(0).getValue());
    
    /*
    排序后,列表为
    aaaa:3
    aaab:3
    zaad:3
    bbbb222:2
    bbbb222:2
    dddd444:1
    dddd445:1
    */
    

    用例:1.同频,不同字典序

    ​ 2.不同频

  • 覆盖率截图

    Lib中没有测试的方法/行

    • Lib用于给WordCount调用处理文件的函数handleFile()和输出函数setOutFile()

    优化覆盖率

    • 尽量一个功能一个方法
    • 但是也不必刻意追求覆盖率,关键是方法的正确性

异常处理

  1. Lib中的异常均为I/O,交由系统处理

  2. WordCount中的main()对于未满两个传入参数的异常,做出输出异常信息,并直接返回

    if (args.length < 2) {
        System.err.println("Require 2 Parameters");
        return;
    }
    Lib lib = new Lib(args[0], args[1]);
    lib.handleFile();
    

心路历程与收获

收获

  • 学习并使用Git和Github
  • 学习并使用JProfiler进行性能测试
  • 学习并使用JUnit框架,进行单元测试
  • 学习并使用Java的正则表达式
  • 了解了阿里巴巴的代码规范,规范了自己的代码习惯
  • 更加熟练地使用Idea进行开发

不足

  • 因为在开发时,没能理清需求和合理规划设计模块,导致开发效率不高,因此导致PSP表格的时间有不小误差
  • 下次开发时,应该顺着PSP表格的步骤走,合理规划、合理设计、看清需求,提高自己的开发效率
posted @ 2021-03-04 14:09  Arturia_Xenon  阅读(129)  评论(4编辑  收藏  举报