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. 计算模块接口部分的性能改进

image
原先的方法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();
    }

在当前代码中,每次遇到运算符时,会进行多次栈操作(出栈和入栈)。可以尝试减少这些栈操作的次数。
可以使用两个临时变量来保存当前运算符和操作数,而不是立即执行计算。
当遇到具有较高优先级的运算符时,可以将其与前一个运算符一起计算,并将结果入栈。

image
可以看到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.非数字字符

覆盖率
image

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);
    }
posted on 2023-09-13 22:37  萨伊拜  阅读(75)  评论(0编辑  收藏  举报