《软件工程》结对作业1----四则运算升级版

1.要求

   第一阶段目标:重构四则运算-- 能把计算的功能封装起来,通过API 接口调用计算方法。定义一个计算核心类:把四则运算的计算功能包装在一个模块中 (这个模块可以是一个类 Class,  一个DLL等等)。“计算核心”模块和调用类它的其他模块之间是什么关系呢?  它们要通过一定的API (Application Programming Interface) 来和其他模块交流。这个API 接口应该怎么设计呢? 可以从下面的最简单的接口开始:Calc()这个Calc 函数接受字符串的输入(字符串里就是运算式子,例如 “ 5+3.5“,  “7/8 – 3/8 ”,  “3 + 90 * (-0.3)“  等等),这个模块的返回值是一个字符串,例如,前面几个例子的结果就是 ( ”17.5“, “ 1/2”, “-24“).

    第二阶段目标 - 通过测试程序和API 接口测试其简单的加减乘除功能。并能看到代码覆盖率。可以扩展 Calc() 的定义,让它接受一个新的参数 “precision”,  或者可以启用一个新的函数 Setting()。最多4 个运算符数值范围是 -1000 到 1000精度是小数点后两位怎么通过API 告诉我们的模块呢?  我们当然可以用函数的参数直接传递,但是参数的组合很多,怎么定义好参数的规范呢?   建议大家考虑用 XML 来传递这些参数。增加了新的Setting() 函数之后,要让模块支持这样的参数,同时,还要保证原来的各个测试用例继续正确地工作。

    第三阶段目标 – 定义异常处理。如果输入是有错误的,例如 “1 ++ 2”, 在数值范围是 -1000 .. 1000 的时候,传进去 “10000 + 32768 * 3”,  或者是 “ 248.04 / 0”  怎么办? 怎么告诉函数的调用者 “你错了”?  把返回的字符串定义为 “-1” 来表示? 那么如果真的计算结果是 “-1” 又怎么处理呢?建议这个时候,要定义各种异常 (Exception), 让 Core 在碰到各种异常情况的时候,能告诉调用者 - 你错了! 当然,这个时候,同样要进行下面的增量修改:定义要增加什么功能 - 例如:支持 “运算式子格式错误” 异常,写好测试用例,传进去一个错误的式子,期望能捕获这个 异常。 如果没有,那测试就报错。在 Core 模块中实现这个功能,测试这个功能, 同时测试所有以前的功能,保证以前的功能还能继续工作 (没有 regression), 确认功能完成,继续下一个功能。

Student.java
package com.az.test.calc;

import java.util.Scanner;


import com.az.test.calc.CreateShiZi;
import com.az.test.calc.NiBoLanShi;

public class Student {

    public static void main(String[] args) {
        CreateShiZi createShiZi = new CreateShiZi();
        NiBoLanShi niBoLanShi = new NiBoLanShi();
        String[] fuHao = {"+","-","*","/"};
        Scanner input = new Scanner(System.in);
        System.out.println("请输入范围内的计算:");
        int m = input.nextInt();
        System.out.println("请输入要产生的题数:");
        int n = input.nextInt();
        String[] strArray = new String[n];
        System.out.println("\n题目\n");
        createShiZi.create(m, n, fuHao, strArray);
        for(int i = 0; i<n; i++) {
            String result = niBoLanShi.cal(strArray[i]);
            System.out.println("第"+(i+1)+"题:"+strArray[i]);
            System.out.print("你的答案:");
            String yourAnswer = input.next();
            if (yourAnswer.equals(result)) {
                System.out.println("True\n");
            }else {
                System.out.println("False");
                System.out.println("正确答案:"+result+"\n");
            }
        }
    }
}

NiBoLanShi.java
package com.az.test.calc;

import java.util.List;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Stack;

public class NiBoLanShi {
    public static String cal(String str) {
        //对表达式进行预处理,并简单验证是否是正确的表达式
        //存放处理后的表达式
        List<String> list = new ArrayList<>();
        char[] arr = str.toCharArray();
        
        //存放数字临时变量
        StringBuffer tmpStr = new StringBuffer();
        for (char c : arr) {
            //如果是数字或小数点,添加到临时变量中
            if (c>='0' && c<='9') {
                tmpStr.append(c);
            }else if(c=='.') {
                if(tmpStr.indexOf(".")>0) {
                    throw new RuntimeException("非法字符");
                }
                tmpStr.append(c);
            }
            
            //如果是加减乘除或者括号,将数字临时变量和运算符依次放入List中
            else if (c=='+' || c=='-' || c=='*' || c=='/' || c=='(' || c==')') {
                if (tmpStr.length() > 0) {
                    list.add(tmpStr.toString());
                    tmpStr.setLength(0);
                }
                list.add(c + "");
            }
            else if (c==' ') {
                continue;
            }
            else {
                throw new RuntimeException("非法字符");
            }
        }
        if (tmpStr.length() > 0) {
            list.add(tmpStr.toString());
        }
        
        //初始化后缀表达式
        List<String> strList = new ArrayList<>();
        
        //运算过程中,使用了两次栈结构,
        //第一次是将中缀表达式转换成后缀表达式,第二次是计算后缀表达式的值
        Stack<String> stack = new Stack<>();
        
        //声明临时变量,存放栈元素
        String tmp;
        
        //将中缀表达式转换成后缀表达式
        for (String s : list) {
            //如果是左括号直接入栈
            if (s.equals("(")) {
                stack.push(s);
            }
            
            //如果是右括号,执行出栈操作,依次添加到后缀表达式中,直到出栈元素为左括号,左括号和右括号都不添加到后缀表达式中
            else if (s.equals(")")) {
                while (!(tmp = stack.pop()).equals("(")) {
                    strList.add(tmp);                    
                }
            }
            
            //如果是加减乘除,弹出所遇优先级大于或等于该运算符的栈顶元素(栈中肯定没有右括号,认为左括号的优先级最低),然后将该运算符入栈
            else if (s.equals("*") || s.equals("/")) {
                while(!stack.isEmpty()) {
                    //取出栈顶元素
                    tmp = stack.peek();//取出但不移除
                    if (tmp.equals("*") || tmp.equals("/")) {
                        stack.pop();
                        strList.add(tmp);
                    }
                    else {
                        break;
                    }
                }
                stack.push(s);
            }
            else if (s.equals("+") || s.equals("-")) {
                while(!stack.isEmpty()) {
                    //取出栈顶元素
                    tmp = stack.peek();
                    if (!tmp.equals("(")) {
                        stack.pop();
                        strList.add(tmp);
                    }
                    else {
                        break;
                    }
                }
                stack.push(s);
            }
            
            //如果是数字,直接添加到后缀表达式中
            else {
                strList.add(s);
            }
        }
        
        //最后依次出栈,放入后缀表达式中
        while (!stack.isEmpty()) {
            strList.add(stack.pop());
        }
        
        //计算后缀表达式的值
        Stack<BigDecimal> newStack = new Stack<>();
        for (String s : strList) {
            //若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符的左边
            //运算后的结果再进栈,直到后缀表达式遍历完毕
            if (s.equals("*") || s.equals("/") || s.equals("+") || s.equals("-")) {
                BigDecimal b1 = newStack.pop();
                BigDecimal b2 = newStack.pop();
                switch (s) {
                case "+":
                    newStack.push(b2.add(b1));
                    break;
                case "-":
                    newStack.push(b2.subtract(b1));
                    break;
                case "*":
                    newStack.push(b2.multiply(b1));
                    break;
                case "/":
                    newStack.push(b2.divide(b1, 9, BigDecimal.ROUND_HALF_UP));
                    break;
                }
            }
            
            //如果是数字,入栈
            else {
                newStack.push(new BigDecimal(s));
            }
        }
        
        //最后,栈中仅有一个元素,就是计算结果
        return newStack.peek().toString();
    }
}

CreateShiZi.java
package com.az.test.calc;


public class CreateShiZi {
    public void create(int m, int n, String[] fuHao, String[] strArray) {
        String str = "";
        
        //随机生成式子
        for (int i = 0; i < n; i++) {
            str = "";
            int[] arr1 = new int[n];
            int[] arr2 = new int[n];
            arr2[i] = (int)(Math.random()*m+1);
            for(int j = 0; j < (int)(Math.random()*10+1); j++) {
                int order = (int)(Math.random()*4);
                arr1[j] = (int)(Math.random()*m+1);
                str = str + arr1[j] + fuHao[order];
            }
            str = str + arr2[i];
            strArray[i] = str;
            System.out.println("第"+(i+1)+"题:"+str);
            arr1 = null;
            arr2 = null;
        }
        System.out.println("\n");
    }
}

 

posted @ 2020-03-07 18:33  Mr_WildFire  阅读(132)  评论(0编辑  收藏  举报