第二次作业
GitHub地址
https://git.coding.net/songofjoy/softwareProject.git
PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
10 |
·Estimate |
· 预估时间 |
10 |
10 |
Development |
开发 |
370 |
330 |
·Analysis |
· 需求分析(包括学习新技术) |
30 |
40 |
·Design Spec |
· 生成设计文档 |
10 |
30 |
·Design Review |
· 设计复审 |
20 |
30 |
·Coding Standard |
· 代码规范 |
40 |
30 |
·Design |
· 具体设计 |
60 |
100 |
·Coding |
· 具体编码 |
220 |
180 |
·Code Review |
· 代码复审 |
30 |
40 |
·Test |
· 测试(自我测试,修改代码,提交修改) |
140 |
170 |
Reporting |
报告 |
250 |
250 |
·Test Report |
· 测试报告 |
220 |
220 |
·Size Measurement |
· 计算工作量 |
20 |
20 |
·Postmortem&Process Improvement Plan |
· 事后总结并提出过程改进计划 |
20 |
30 |
合计 |
|
630 |
830 |
需求分析
目标
做一个多功能的四则混合运算器
特性
1.支持分数的四则运算
2.可以实现打分功能
3.计时功能
4.储存历史得分记录
5.实现了中英文繁体的切换
面向用户
主要用于小学生使用,通过这样的一个四则运算工具,可以进行打分测评,可以用来训练计算准确率
假定约束
开发方法:面向对象的开发技术
开发语言:Java
开发期限:1周
需求规定
1.表达式计算:在文本框输入一个表达式,可以返回结果
2.表达式计算判断功能:在文本框输入一个含有=的式子,可以返回是否计算正确
3.中英简繁体转化功能:可以实现java语言的国际化
4.计分功能:可以用来记录历史得分情况
5.计时功能:可以用来计时
6.GUI用户界面:可以更方便的进行用户操作
运行环境规定
设备:PC
支持:PC支持Windows操作系统
控制:本软件主要GUI界面运行
详细设计
设计思路
1.将表达式进行数字运算符分离,存入数组
2.通过1中的数组进行一个中缀到逆波兰表达式额转换,运用了双栈来完成
3.分别编写加减乘除等函数
4.根据辗转相除法实现一个求最大公约数、最小公倍数的函数
5.编写判断函数,判断式子是否正确
6.读写文件的函数实现,关闭系统时可以将计算正确率、正确题目等信息保存至文件,下次打开自动调用
7.计时功能、计算正确率统计,中英文繁体转换的函数实现
8.用户交互界面:制作了一个简单的UI界面,使操作更加方便。
用户交互界面展示
界面说明
1.右下角是可以实现简体繁体英文的切换。
2.左下角是历史的正确率、正确题目和错误题目,关闭系统数据依然保留。
3.“重置”按钮是使正确率、正确题目和错误题目归0。
4.“清除”按钮是使TextField清空。
5.“退格”按钮是使TextField删除末尾的字符。
6.’=’与=的区别:
(1)’=’指在TextField添加一个’=’字符到末尾。
(2)= 指运算或者判断对错,如果TextField里面没有含=,则计算表达式的结果;如果TextField里面含=,就判断该式子是否正确。
7.“计”实现了计时功能,第一次点击是开始计时,第二此点是停止,第三次点是清0.
核心代码说明
ComputeController.java 加减乘除计算控制器
package calculator;
/**
* 加减乘除计算
* 冒号:表示一个分数,左边是分子,右边是分母
*
* @author Song
*
*/
public class ComputeController {
/**
* 执行计算
* @param data1
* @param data2
* @param sign
* @return
*/
public static String exec(String a1,String a2,String sign){
String retString="";
switch (sign) {
case "+":retString=add(a1, a2);break;
case "-":retString=substract(a1, a2);break;
case "×":retString=mutiply(a1, a2);break;
case "÷":retString=devide(a1, a2);break;
}
return retString;
}
/**
* 是否是加减乘除运算符
* @param string
* @return
*/
public static boolean isSign(String string){
if(string.equals("+")||string.equals("-")||
string.equals("×")||string.equals("÷")){
return true;
}
return false;
}
/**
* 求最大公约数
* @param a1
* @param a2
* @return
*/
public static int gcd(int a1,int a2){
if(a1==0||a2==0){System.out.println("Error:the data is zero.");}
int min=a1;
int max=a1;
if(a1<a2){
max=a2;
}
else{
min=a2;
}
if(max%min==0){
return min;
}
int temp=max%min;
max=min;
min=temp;
while(max%min!=0){
temp=max%min;
max=min;
min=temp;
}
return min;
}
/**
* 求最小公倍数
* @param a1
* @param a2
* @return
*/
public static int lcm(int a1,int a2){
if(a1==0||a2==0){System.out.println("Error:the data is zero.");}
int gcd=gcd(a1,a2);
return a1/gcd*a2;
}
/**
* stringdata转化为分数的表示形式
* @param data
* @return
*/
public static int [] string2fac(String data){
int [] retfac=new int[2];
if(data.contains("/")){
String strings[]=data.split("/");
retfac[0]=Integer.valueOf(strings[0]);
retfac[1]=Integer.valueOf(strings[1]);
}
else{
int integer=Integer.valueOf(data);
retfac[0]=integer;
retfac[1]=1;
}
return retfac;
}
DoubleStack.java 处理计算优先级(双栈和逆波兰表达式)
package calculator;
import java.util.ArrayList;
import java.util.Stack;
/**
* 高级软件工程
* @author Song
*
*/
public class DoubleStack {
private ArrayList<String> testString=new ArrayList<>();
private Stack<String> stack = null;
private ArrayList<String> createStrings=new ArrayList<>();
public DoubleStack(String testString) {
this.testString = string2array(testString);
this.stack = new Stack<String>();
}
/**
* 打印逆波兰表达式和
*/
public void printCreString(){
System.out.println(createStrings);
}
/**
* 根据逆波兰表达式计算值
*/
public String compute(){
String ERRORINFO="The Function Is Wrong!";
try{
if(createStrings==null||createStrings.size()==0){
return ERRORINFO;
}
while(createStrings.size()!=1){
int i;
for(i=0;i<createStrings.size();i++){
if(ComputeController.isSign(createStrings.get(i))){
if(i-2<0){return ERRORINFO;}
String compute=ComputeController.exec(createStrings.get(i-2), createStrings.get(i-1), createStrings.get(i));
createStrings.set(i-2, compute);
createStrings.remove(i);
createStrings.remove(i-1);
break;
}
else if(i==createStrings.size()-1){return ERRORINFO;}
}
}
}
catch(Exception e){
return ERRORINFO;
}
return createStrings.get(0);
}
/**
* 逆波兰 双栈
*/
public ArrayList<String> analysisString() {
for (int i = 0; i < testString.size(); i++) {
String c = testString.get(i);
if (c.equals("+") || c.equals("-")) {
if (stack.isEmpty() || stack.peek().equals("(")) {
stack.push(c);
} else {
while (!stack.isEmpty()
&& (stack.peek().equals("×") || stack.peek().equals("÷")
|| stack.peek().equals("+") || stack.peek().equals("-"))) {
createStrings.add(stack.pop());
}
stack.push(c);
}
} else if (c.equals("×")|| c.equals("÷")) {
if (stack.isEmpty() || stack.peek().equals("+")
|| stack.peek().equals("-") || stack.peek().equals("(")) {
stack.push(c);
} else {
while (!stack.isEmpty()
&& (stack.peek().equals("÷") || stack.peek().equals("×"))) {
createStrings.add(stack.pop());
}
stack.push(c);
}
} else if (c.equals("(")) {
stack.push(c);
} else if (c.equals(")") ){
String temp = "";
while (!(temp = stack.pop()).equals("(")) {
createStrings.add(temp);
}
} else {
createStrings.add(c);
}
}
if (!stack.isEmpty()) {
while (!stack.isEmpty()) {
createStrings.add(stack.pop());
}
}
return createStrings;
}
/**
* string转array
* @param input
* @return
*/
public static ArrayList<String> string2array(String input){
String curString="";
ArrayList<String> strings=new ArrayList<>();
for(int i=0;i<input.length();i++){
char curchar=input.charAt(i);
if(curchar<58&&curchar>47||curchar=='/'){curString+=curchar;}
else{
if(!curString.equals("")){strings.add(curString);}
strings.add(""+curchar);curString="";
}
}
if(!curString.equals("")) {strings.add(curString);}
return strings;
}
}
Frame.java 简易GUI用户界面
package calculator;
import java.awt.EventQueue;
public class Frame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JTextField textField;
JLabel lblNewLabel_1 = new JLabel("0");
JLabel label_2 = new JLabel("0");
JLabel label_3 = new JLabel("0.0%");
static JLabel lblNewLabel = new JLabel("正确题目");
static JLabel label = new JLabel("错误题目");
static JLabel label_1 = new JLabel("正确率");
static JButton button_20 = new JButton("重置");
static JButton button_19 = new JButton("清除");
static JButton button_18 = new JButton("退格");
static JButton button_23 = new JButton("简");
static JButton button_21 = new JButton("繁");
static JButton button_22 = new JButton("英");
static final String EN="resource_english";
static final String CH="resource_zh_CN_1";
static final String HARD="resource_hard";
static Score curScore=new Score();
DecimalFormat df= new DecimalFormat("######0.0");
/**
* 字体中英繁切换
* @param resource
*/
public static void changeCharacter(String resource){
ResourceBundle rb = ResourceBundle.getBundle(resource);
lblNewLabel.setText(rb.getString("rightAmount"));
label.setText(rb.getString("wrongAmount"));
label_1.setText(rb.getString("radioAmount"));
button_20.setText(rb.getString("reset"));
button_19.setText(rb.getString("clear"));
button_18.setText(rb.getString("back"));
button_23.setText(rb.getString("simply"));
button_21.setText(rb.getString("hard"));
button_22.setText(rb.getString("english"));
.......
}
Score.java 评分模型
package calculator;
public class Score {
private int rightAmount;
private int wrongAmount;
private double radioAmount;
public int getRightAmount() {
return rightAmount;
}
public void setRightAmount(int rightAmount) {
this.rightAmount = rightAmount;
}
public int getWrongAmount() {
return wrongAmount;
}
public void setWrongAmount(int wrongAmount) {
this.wrongAmount = wrongAmount;
}
public double getRadioAmount() {
return radioAmount;
}
public void setRadioAmount(double radioAmount) {
this.radioAmount = radioAmount;
}
}
Util.java 公共类(文件的读取写入)
package calculator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.Date;
public class Util {
static String FILENAME="score.dat";
public static String SPLITSTRING = "&HHS&";
//创建Score
public static void createScore() throws IOException {
Score score=new Score();
FileWriter writer = new FileWriter(FILENAME);
writer.write(score.getRightAmount() + SPLITSTRING);
writer.write(score.getWrongAmount() + SPLITSTRING);
writer.write(score.getRadioAmount() + SPLITSTRING);
writer.close();
}
//读取分数
public static Score readScore() throws IOException, ParseException {
Score score=new Score();
BufferedReader reader = new BufferedReader(new FileReader(FILENAME));
String line = reader.readLine();
String []infos=line.split(SPLITSTRING);
score.setRightAmount(Integer.valueOf(infos[0]));
score.setWrongAmount(Integer.valueOf(infos[1]));
score.setRadioAmount(Double.valueOf(infos[2]));
return score;
}
//写分数
public static void saveScore(Score score) throws IOException, ParseException {
FileWriter writer = new FileWriter(FILENAME);
writer.write(score.getRightAmount() + SPLITSTRING);
writer.write(score.getWrongAmount() + SPLITSTRING);
writer.write(score.getRadioAmount() + SPLITSTRING);
writer.close();
}
/**
*
* 获取目录下的所有文件名 By Hhs
*/
public static String[] getFileName(String path) {
File file = new File(path);
String[] fileName = file.list();
return fileName;
}
}
resource_zh_CN_1.properties等配置文件
实现了java语言的国际化,可以实现中文英文简体繁体的切换。
项目小结
这个作业功能是实现了,但是有些细节还需要改进,比如当计算结果取=时,如果输入下一个式子,TextField里面的内容不会自动清空,后期可以对这个进行一个优化,加一个字段的判断,如果按下=,该字段置为true,表示下个字符如果是数字就首先把TextField清空再放入数据。
在测试的时候出现一些小bug,比如对负数的处理等等,会得到令人意想不到的结果,当输入的表达式不合法时也加入了一个异常处理,返回The Function is Wrong!给前台界面,后期可以对此做改进,将异常返回做的细一点,比如是分母不能为0或者是数字太大或者是表达式里面有特殊字符,将错误信息尽可能细的返回给用户。