GIT地址 | GITHUB克隆地址 |
---|---|
结对伙伴 | 余睿哲 |
一、PSP表格
二、接口的设计与实现过程
设计:
共五大类,其中计算方法类和命令行翻译类两个类需要设计对应的接口。
详细内容见下面思维导图(如果图片看不清可以适当放大网页观看)
各类之间的关系
CountMethodImpl类 为Main类提供各种算法
TranslateCommandImpl类 则翻译Main类传过去的命令行指令,并返回需要的参数
WriteFile类 主要用于文件读写
Test类 用于测试各种类中的方法能否正确实现
Main类 中执行主函数,集成所有功能。
关键函数及实现方法(见思维导图)
程序运行结果
注:有效行数与看到的行数不同的原因:文档窗口的大小不同,会导致显示的行数不同,如果文档长,则该文章(测试文章)可显示为一行
用到的几种设计理念
Interface Design
为两个比较重要,且内部函数较多的类设计了接口
Loose Coupling
设计了五大类,将各个部分分开,想要加减功能可以在对应的类中进行更新,对整体影响较小
Information Hiding
使用一些常量将ASCII码、条件语句以及循环的参数保存起来,即隐藏了数据也提高了代码的可读性。
三、代码复审
编码规范
参考《阿里巴巴JAVA开发手册》
IDEA编译器可以下载审查插件
插件扫描结果,即还存在的问题:
改正后):
还存在一定的问题是因为:1、有一个类为最早写的测试类,里面有较多直接使用的数字(以后会多加注意)。2、有变量定义为numberM和numberN,是符合小写驼峰命名法的,是插件扫描判断错误。
四、性能改进
性能改进
接口实现的函数中,大多数都需要用到循环,循环的时间复杂度不算多,但也不算少。所以在循环中减少不必要的循环,不如在需要跳出循环时跳出循环,减少循环次数等等,即可减少一定的时间消耗,从而提高性能。
性能工具:JProfiler(IDEA的性能测试插件)
安装方法
性能分析图
主函数因为集成了所有工具类的方法,所以消耗最大
主函数代码如下
public static void main(String[] args) { //输入命令行 Scanner scanner = new Scanner(System.in); String cmd = scanner.nextLine(); //创建需要的对象 TranslateCommandImpl translate = new TranslateCommandImpl(); CountMethodImpl method = new CountMethodImpl(); WriteFile writeFile = new WriteFile(); //得到需要的参数 String inFile = translate.returnInFile(cmd); String outFile = translate.returnOutFile(cmd); int numberM = translate.returnNumberM(cmd); int numberN = translate.returnNumberN(cmd); //能否读到文件,不能则停止 try { InputStream in = new FileInputStream(inFile); }catch (IOException e) { System.out.println("文件位置有误!"); return; } //用于保存各种数据 int characters, words, lines; //词频Map中的长度 int length; //判断有无-i, -o指令 String noFile = "null"; if(noFile.equals(inFile) || noFile.equals(outFile)) { System.out.println("输入或者输出不能为空!"); return; } //用于保存单词集 List<String> strings; //定长单词集 List<String> lengthWords; characters = method.countCharacterNumber(inFile); strings = method.countWordNumber(inFile); words = strings.size(); lines = method.countLine(inFile); lengthWords = method.statisticsWord(strings, numberM); //得到词频的Map List<Map.Entry<String, Integer>> frequency = method.countWordsFrequency(strings); //保存定长词频 List<Map.Entry<String, Integer>> neededFrequency = new ArrayList<>(); //得到其长度,方便遍历 int maxLength = 10; length = frequency.size(); //如果长度大于10,则把长度变成10 if(length > maxLength) { length = 10; } //打印结果 System.out.println("字符总数是:"+characters); System.out.println("有效单词数是:"+words); System.out.println("有效行数是:"+lines); System.out.println("单词的出现频率从高到低依次是:"); for(int i=0; i<length; i++) { System.out.println(frequency.get(i)); } if(numberM > 0) { System.out.println("定长为 "+numberM+" 的单词是:"+lengthWords); } //如果参数n大于Map中的大小,则打印有效个。 if(numberN > length) { numberN = length; } if(numberN > 0) { System.out.println("出现评率最高的前"+numberN+"个单词依次是:"); for(int i=0; i<numberN; i++) { System.out.println(frequency.get(i)); neededFrequency.add(frequency.get(i)); } } //写入文件 writeFile.writeFile(outFile, characters, words, lines, lengthWords, neededFrequency); }
五、单元测试展示
测试有效行的计算
函数构造思路:得到数据后,打印文章行数
数据构造思路:文章中有空行,看程序能否正确判断
测试有效单词的读取
函数构造思路:得到数据后,统计有效单词数后打印
数据构造思路:文章中有部分非有效单词,看程序能否成功判断并存入单词集合中。
测试命令行参数的获取
函数构造思路:给定一条命令行指令,看能否正确地获得参数
数据构造思路:给定各种形式的命令行进行测试,看能否正确获得参数;以及命令行有问题时能否正确报错。
六、异常处理说明
注:异常处理都在主函数中直接进行测试
异常一:命令行没有 -i 或 -o指令
异常二:无法读取到文件
七、结对过程
描述:
这次很幸运能够找到也同样是写JAVA的同学余睿哲。分工明确,因为我在一开始就对这个程序的编写有比较明确的思路,所有完成了大部分代码的编写;相应的他完成了很多代码的审计工作。结对编程让我感到一个复杂程序的编写,因为两个人的合作与努力变得简单了。结对期间没有发生任何矛盾,每个人都把自己的做的那部分任务做到了最好。代码的编写期间也有过阻碍,通过两个人的共同思考解决了问题。让我真切的感受到1+1>2。