1. [github链接]
(https://github.com/2082486126/Calculator "github链接")
2. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
计划 | · 估计这个任务需要多少时间 | 120 | 90 |
Development | 开发 | 30 | 15 |
· Analysis | · 需求分析 (包括学习新技术) | 10 | 5 |
· Design Spec | · 生成设计文档 | 10 | 5 |
· Design Review | · 设计复审 | 10 | 5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | |
· Design | · 具体设计 | 20 | 5 |
· Coding | · 具体编码 | 20 | 5 |
· Code Review | · 代码复审 | 5 | 5 |
· Test | · 测试(自我测试,修改代码,提交修改) | 5 | 5 |
Reporting | 报告 | 20 | 20 |
· Test Repor | · 测试报告 | 5 | 5 |
· Size Measurement | · 计算工作量 | 5 | 5 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 5 | 5 |
· 合计 | 165 | 90 |
3.计算模块接口的设计与实现过程
设计模块接口:
定义一个Solver接口,作为计算器求解器的抽象。该接口应该包含一个方法solve(String formula),用于接收一个数学公式并返回计算结果或错误信息。
实现模块接口:
创建一个实现Solver接口的类,例如MathSolver。
在MathSolver类中实现solve方法:
检查输入的公式是否为空。如果为空,返回错误信息:"Invalid formula"。
创建一个操作数栈numStack和一个操作符栈opStack,用于执行计算。
遍历公式的每个字符:
如果是数字字符,则将其添加到一个临时字符串中,以便后续解析为操作数。
如果是运算符字符(+、-、*、/):
如果临时字符串中存在数字,将其解析为操作数并压入numStack栈中。
通过比较运算符的优先级,将运算符压入opStack栈中。
如果是其他无效字符,则返回错误信息:"Invalid formula"。
如果临时字符串中存在数字,将其解析为操作数并压入numStack栈中。
执行栈中剩余的运算符和操作数的计算,直到栈为空。
如果在计算过程中出现除以零的情况,抛出ArithmeticException异常,捕获该异常并返回错误信息:"Division by zero"。
返回计算结果。
使用模块接口:
在您的应用程序中,通过创建MathSolver对象来使用计算器求解器。
调用MathSolver对象的solve方法,传入要解析的数学公式。
根据返回的结果进行处理,例如打印结果或处理错误信息。
4. 计算模块接口部分的性能改进
原先的方法cpu占用率高达87%
于是采用这种办法解决:
点击查看代码
public static String Solve(String formula) {
if (formula.isEmpty()) {
return "Invalid formula";
}
Stack<Integer> numStack = new Stack<>();
Stack<Character> opStack = new Stack<>();
StringBuilder numBuilder = new StringBuilder();
for (int i = 0; i < formula.length(); i++) {
char ch = formula.charAt(i);
if (Character.isDigit(ch)) {
numBuilder.append(ch);
} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
if (numBuilder.length() > 0) {
int num = Integer.parseInt(numBuilder.toString());
numStack.push(num);
numBuilder.setLength(0);
}
while (!opStack.empty() && hasPrecedence(ch, opStack.peek())) {
char op = opStack.pop();
int num2 = numStack.pop();
int num1 = numStack.pop();
int result = applyOperator(num1, num2, op);
numStack.push(result);
}
opStack.push(ch);
} else {
// Invalid character found
return "Invalid formula";
}
}
if (numBuilder.length() > 0) {
int num = Integer.parseInt(numBuilder.toString());
numStack.push(num);
}
while (!opStack.empty()) {
char op = opStack.pop();
int num2 = numStack.pop();
int num1 = numStack.pop();
int result = applyOperator(num1, num2, op);
numStack.push(result);
}
return formula + "=" + numStack.pop();
}
在当前代码中,每次遇到运算符时,会进行多次栈操作(出栈和入栈)。可以尝试减少这些栈操作的次数。
可以使用两个临时变量来保存当前运算符和操作数,而不是立即执行计算。
当遇到具有较高优先级的运算符时,可以将其与前一个运算符一起计算,并将结果入栈。
可以看到Main.slove方法的CPU使用率用到了66.36%
5. 计算模块部分单元测试展示
点击查看代码
public class MainTest {
@Test
public void solve() {
String sum = Main.Solve("11+22");
Assert.assertEquals("11+22=33", sum);
}
@Test
public void testMakeFormula() {
String formula = Main.MakeFormula();
Assert.assertNotNull(formula);
Assert.assertFalse(formula.isEmpty());
Assert.assertTrue(formula.matches("\\d+([+\\-*/]\\d+)*")); // 校验是否符合题目要求的格式
}
@Test
public void testSolve() {
String formula = "3+5*2-4";
String result = Main.Solve(formula);
Assert.assertEquals("3+5*2-4=9", result);
}
@Test
public void testSolveWithDivision() {
String formula = "6/2+4";
String result = Main.Solve(formula);
Assert.assertEquals("6/2+4=7", result);
}
@Test
public void testSolveWithNegativeResult() {
String formula = "2-5";
String result = Main.Solve(formula);
Assert.assertEquals("2-5=-3", result);
}
@Test
public void testSolveWithZeroResult() {
String formula = "5-5";
String result = Main.Solve(formula);
Assert.assertEquals("5-5=0", result);
}
@Test
public void testSolveWithZeroDivision() {
String formula = "4/0";
String result = Main.Solve(formula);
Assert.assertEquals("Division by zero", result);
}
@Test
public void testSolveWithExtraSpaces() {
String formula = "3+5*2-4";
String result = Main.Solve(formula);
Assert.assertEquals("3+5*2-4=9", result);
}
@Test
public void testSolveWithInvalidCharacters() {
String formula = "3+5abc*2-4";
String result = Main.Solve(formula);
Assert.assertEquals("Invalid formula", result);
}
@Test
public void testSolveWithEmptyFormula() {
String formula = "";
String result = Main.Solve(formula);
Assert.assertEquals("Invalid formula", result);
}
@Test
public void testProfile(){
for(int i = 0;i < 10000000; i++) {
String question = Main.MakeFormula();
String ret = Main.Solve(question);
}
}
}
思路:
1.考虑特殊情况
2.非数字字符
覆盖率
6. 计算模块部分异常处理说明
1. ArithmeticException
数学异常,适用于除数为0的情况
点击查看代码
@Test
public void testSolveWithZeroDivision() {
String formula = "4/0";
String result = Main.Solve(formula);
Assert.assertEquals("Division by zero", result);
}
2.IllegalArgumentException
非法参数异常,除了数字外的其他符号
点击查看代码
@Test
public void testSolveWithInvalidCharacters() {
String formula = "3+5abc*2-4";
String result = Main.Solve(formula);
Assert.assertEquals("Invalid formula", result);
}