Coding.net仓库地址:https://coding.net/u/zhh1011/p/QuestionMaker/githtml
克隆地址:https://git.coding.net/zhh1011/QuestionMaker.gitjava
测试步骤:git
1.进入src文件夹算法
2.在命令行输入javac -encoding utf-8 Main.java设计模式
3.回车再输入java Main 20架构
4.回车,将会在根目录下(与src同级)产生result.txtide
说在前面:本篇博文用于提交本人课程做业,若读者有兴趣也可访问https://edu.cnblogs.com/campus/nenu/2016SE_NENU/homework/1656了解做业要求。模块化
1、需求分析单元测试
从题目中的基本要求以下:学习
一、输入n,能输出n个四则运算题目在与main文件同目录的“result.txt”文件中;
二、输出的题目中,数字在0~100之间,运算符在3~5个之间;
三、输出的题目需含有答案,且答案不得包含负数与分数;
四、题目在运算中不得包含负数与分数;
2、功能设计
可以根据用户输入的参数n随机产生n道符合要求的练习题,自动算出答案,并将式子与答案以文档的形式呈现。
3、设计实现
在个人src文件下共有两个包以及Main类:
首先描述一下Main类:
Main类的main方法:
public static void main(String[] args) { //用于规范用户输入的部分--------- try { NumberException exception = new NumberException(); times = Integer.valueOf(args[0]); times = exception.testNumber(times); }catch (Exception e){ System.out.println("请输入1~1000的数字"); times = inPut(); } //--------------------------- Outer outer = new Outer(times); }
以及用于递归来规范用户输入的inPut方法:
//递归方法,用于规范用户输出 private static int inPut(){ try { //自定义的异常,用于判断输入是否处于1~1000 NumberException exception = new NumberException(); Scanner input = new Scanner(System.in); times = input.nextInt(); times = exception.testNumber(times); }catch (Exception e){ System.out.println("请输入1~1000的数字"); inPut(); } return times; }
conifg包下放着静态的数据如学号,姓名等,以及自定义的用于限制用户输入的异常类,并不复杂,在这里就不加赘述了。
main包下放着entity包与worker包:
entity包内放着题目的实体类Question类:
在这里我要展现一下toString()方法:
//规范化输出题目字符串
@Override public String toString() { Object[] box = new Object[nums*2+4]; box[nums*2+1] = '='; box[nums*2+2] = results; box[nums*2+3] = "\r\n"; for(int i = 0; i < nums*2+1;i=i+2){ box[i] = number[i/2]; } for(int i = 1; i < nums*2;i=i+2){ box[i] = chars[i/2]; } return Arrays.toString(box).replace('[',' ').replace(']',' ').replace(',',' '); }
worker包内有这三个类:
Maker类用于制造完整的题目并调用Tester类的对象对题目进行测试(并在测试过程当中获取答案):
在这里就简单的展现一下它的结构(由于真的没啥说的)
Tester类是整个项目里相对来说最复杂的模块,我会放在代码展现部分解释运算部分,在这里介绍我用到的构建方式:
下面是Test类的数据域、构造器以及方法:
以及运算模块(我在这里练习了一下Java里的“策略模式”)
最后是Outer类:
最后在Outer的构造器里直接完成输出:
//Outer构造器
public Outer(int times){
this.times = times;
this.boxs = new String[times+1];
Question question;
boxs[0] = ID;
for (int i = 1;i <= times;){
maker.makeQuestion();
maker.testQuestion();
if(!maker.getQuestion().isUseful())
continue;
String box = maker.getQuestion().toString();
boxs[i] = box;
i++;
}
//在这里写入"result.txt"文件
outQuestion(boxs);
}
4、算法实现
算法很简单(甚至能够说没有),只是运用java本身的链表(ArrayList与List)自带的方法和递归完成简单的运算,以及if-else判断算符的优先级,使用策略模式优化代码结构,并无使用所谓的调度场算法或者其余一些算法来实现。
5、测试运行
首先编译源文件(需转换为UTF-8进行编码,不然会中文报错),而后进行非法输入测试:
进行正确输入后检查输出是否正确:
6、展现部分代码
我本次项目不像其余人或拥有高超的算法或实现了更多的功能,我最满意的地方在于本身在此次项目里对Java程序设计的一种实践。经过继承、组合,用不少包、类,甚至采用了策略设计模式去模块化本身的代码,更简易的去修改去改进,添加更多的注释去帮助别人理解本身的代码并进行再开发。
举个例子好比有关运算部分的代码,使用了策略设计模式去模块化运算操做:
1 //这一部分属于Test类中的方法----------------------------------------- 2 //策略模式的方法
3 private Box function(Function f, Box box){ 4 return f.run(box); 5 } 6
7 //计算题目的答案
8 private int getResult(Box box){ 9 //用于判断优先级的部分
10 if(box.listC.indexOf('*')>box.listC.indexOf('÷')) 11 box = function(new Multiplication(),box); 12 else if(!(box.listC.indexOf('÷')==(-1))) 13 box = function(new Division(),box); 14 else if(box.listC.get(0) == '+') 15 box = function(new Plus(),box); 16 else if(box.listC.get(0) == '-'){ 17 box = function(new Subtraction(),box); 18 } 19 //实现递归的部分
20 if(box.listC.isEmpty()){ 21 if(box.listN.get(0)==-1) 22 questionTest.setUseful(false); 23 return box.listN.get(0); 24 } 25 else { 26 return getResult(box); 27 } 28 } 29 //这一部分属于Test类中的方法----------------------------------------- 30 //策略部分开始--------------------------------------------|
31 class Function{ 32 Box run(Box box){ 33 return box; 34 } 35 } 36
37 //乘法运算部分
38 class Multiplication extends Function{ 39 @Override 40 Box run(Box box){ 41 box.listN.set(box.listC.indexOf('*'),box.listN.get(box.listC.indexOf('*'))*box.listN.get(box.listC.indexOf('*')+1)); 42 box.listN.remove(box.listC.indexOf('*')+1); 43 box.listC.remove(box.listC.indexOf('*')); 44 return box; 45 } 46 } 47
48 //除法运算部分
49 class Division extends Function{ 50 @Override 51 Box run(Box box) { 52 if(!(box.listN.get(box.listC.indexOf('÷')+1)==0)&&box.listN.get(box.listC.indexOf('÷'))%box.listN.get(box.listC.indexOf('÷')+1) == 0){ 53 box.listN.set(box.listC.indexOf('÷'),box.listN.get(box.listC.indexOf('÷'))/box.listN.get(box.listC.indexOf('÷')+1)); 54 box.listN.remove(box.listC.indexOf('÷')+1); 55 box.listC.remove(box.listC.indexOf('÷')); 56 } 57 else{ 58 //答案非法,直接中止递归
59 box.listC.clear(); 60 box.listN.set(0,-1); 61 return box; 62 } 63 return box; 64 } 65 } 66
67 //加法部分
68 class Plus extends Function{ 69 @Override 70 Box run(Box box) { 71 box.listN.set(box.listC.indexOf('+'),box.listN.get(box.listC.indexOf('+'))+box.listN.get(box.listC.indexOf('+')+1)); 72 box.listN.remove(box.listC.indexOf('+')+1); 73 box.listC.remove(box.listC.indexOf('+')); 74 return box; 75 } 76 } 77
78 //减法运算部分
79 class Subtraction extends Function{ 80 @Override 81 Box run(Box box) { 82 if(box.listN.get(box.listC.indexOf('-'))-box.listN.get(box.listC.indexOf('-')+1) > 0) { 83 box.listN.set(box.listC.indexOf('-'), box.listN.get(box.listC.indexOf('-')) - box.listN.get(box.listC.indexOf('-') + 1)); 84 box.listN.remove(box.listC.indexOf('-') + 1); 85 box.listC.remove(box.listC.indexOf('-')); 86 } 87 else { 88 //答案非法,直接中止递归
89 box.listC.clear(); 90 box.listN.set(0,-1); 91 return box; 92 } 93 return box; 94 } 95 } 96 //策略部分结束----------------------------------------| 97
98 //封装数字与运算符的盒子(Box...)
99 class Box{ 100 List<Integer> listN = new ArrayList<>(); 101 List<Character> listC = new ArrayList<>(); 102
103 Box(List<Integer> listN,List<Character> listC){ 104 this.listC = listC; 105 this.listN = listN; 106 } 107 }
这些都让我感受更...更温馨,不去直观的面对真正的实现与细节,而是经过组合,继承,建立一个对象去调用它的方法去实现功能。让我本身思路清晰,明白本身须要什么,明白本身要干什么。我能够随时完成一个小模块的实现,也能够在平常的各类活动中,去思考一个小模块如何去实现,利用起碎片化的时间,甚至有时候灵机一动解决一个模块里的小问题。在后期修改错误阶段也能够很简单就改变一个模块的行为,由于它并不会牵扯不少东西,只须要修改调用它的模块就好了,它处理的数值它内部的变化丝绝不会影响其余东西。
7、总结
为了让本身的代码模块化,首先我给问题创建了实体类来封装有关问题的信息(包含的数字,字符,字符数量等等),随后将功能模块化,生成与测试分离,输出与生成分离。且有关问题字符串形式的输出也是在Question类的toString()方法中实现的。输出(Outer)模块仅仅是负责调用生成模块生成符合的问题对象并规范输出,生成模块(Maker)也只是负责随机生成题目对象而后调用测试模块去测试并同时生成答案,测试(Tester)模块内部Tester类本身只负责测试,运算部分由整个Function类及其子类完成。
PSP2.1 |
任务内容 |
计划共完成须要的时间(h) |
实际完成须要的时间(h) |
Planning |
计划 |
1.5 |
3 |
· Estimate |
· 估计这个任务须要多少时间,并规划大体工做步骤 |
1.5 |
3 |
Development |
开发 |
20 |
22 |
· Analysis |
· 需求分析 (包括学习新技术) |
2 |
3 |
· Design Spec |
· 生成设计文档 |
0 |
0.5 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
0 |
0.5 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
0 |
0 |
· Design |
· 具体设计 |
2 |
3 |
· Coding |
· 具体编码 |
15 |
11
|
· Code Review |
· 代码复审 |
0 |
1 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
1 |
4 |
Reporting |
报告 |
3 |
4 |
· Test Report |
· 测试报告 |
0.5 |
0 |
· Size Measurement |
· 计算工做量 |
0.5 |
0.5 |
· Postmortem & Process Improvement Plan |
· 过后总结, 并提出过程改进计划 |
2 |
3.5
|
最令我意外的当属开发最后的测试阶段了,错误层出不断,许多地方和本身想象的不同。对不少东西进行了大改,对最初的架构作了很大的改变(最初的架构能够在我以前博客:http://www.cnblogs.com/zanghh/p/8604724.html),设计和现实的差距真的很大,只有在运行和测试的时候才能发现(测试并无很规矩的去单元测试,只是写了一些很简单的方法本身去看输出是否符合预期,相关代码能够在test文件下看到)。其次就是计划阶段,断断续续一直在想那些,觉得一个小时就能解决的东西,大概想了有两三个小时,只能怪本身一直想着如何优化了(实际最后都没去优化)。总的来讲此次的实践让我受益不浅,有机会去实践一些本身之前学到的一些设计方式和思想,也看了不少新的设计理念。感觉一项目从无到有的痛苦与愉悦(写不出来的痛苦、项目完成的那一刻的激动真的无语言表...后来的测试又是当头一棒...)。真的是学会了不少复习了不少实践了不少(甚至打开了有半年多没看过的API文档),若是你是一名后来者,我真诚的建议你去本身敲一个从无到有的项目,并按正常的流程开发,绝对是一次不可多的体验(特别是第一次的话),最后也但愿若是你也这样作得话,也请把本身的经验也写成一篇博客与人分享,让这种体验传播的更为普遍。