zoukankan      html  css  js  c++  java
  • 双人结对,四则运算(三阶段)

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

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

    3第三阶段目标 – 定义异常处理。

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

    package com.core.bean;
    
    import java.math.BigDecimal;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Stack;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import com.core.util.JaxbUtil;
    
    public class Calculate {
        
        private static final Pattern EXPRESSION_PATTERN = Pattern.compile("[0-9\.+-/*()= ]+");
    
        private static final Map<String,Integer> OP_PRIORITY_MAP = new HashMap<String ,Integer>() {
            /**
             * 
             */
            private static final long serialVersionUID = -5028404412583819744L;
    
            {
                put("(",0);
                put("+",2);
                put("-",2);
                put("*",3);
                put("/",3);
                put(")",7);
                put("=",20);
            }
        };
        
        //不带xml参数的计算
        public static String Calc(String expression) throws MyException {
            
            //判断表达式是否为空
            if(expression==null||expression.equals("")) {
                throw new IllegalArgumentException("表达式不能为空!");
            }
            
            Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
            if(!matcher.matches()){
                throw new IllegalArgumentException("表达式含有非法字符!");
            }
            
            expression = formatExpression(expression);
            isExpression(expression);
            
            Stack<String> optstack = new Stack<>();
            Stack<BigDecimal> numStack = new Stack<>();
            StringBuilder numBuilder = new StringBuilder(16);
            char pre;
            
            
            
            for(int i=0;i<expression.length();i++) {
                char ch = expression.charAt(i);
                
                if(ch!=' ') {
                    pre = ch;
                    
                    if((ch>='0'&&ch<='9')||ch=='.') {
                        numBuilder.append(ch);
                    }else {
                        if(numBuilder.length()>0) {
                            if(isNum(numBuilder.toString())==false) {
                                throw new MyException("表达式格式错误!————数字格式错误(错误数字:'"+numBuilder+"'");
                            }
                            numStack.push(new BigDecimal(numBuilder.toString()));
                            numBuilder.delete(0, numBuilder.length());
                        }
                        String operation = String.valueOf(ch);
                        if(optstack.empty()) {
                            optstack.push(operation);
                        }else {
                            if(operation.equals("(")) {
                                optstack.push(operation);
                            }else if(operation.equals(")")) {
                                directCal(optstack, numStack,true);
                            }else if(operation.equals("=")) {
                                directCal(optstack, numStack,false);
                                return numStack.pop().toString();
                            }else {
                                compareOpt_Cal(numStack, optstack, operation);
                            }
                        }
                    }
                }
            }
            
            if(numBuilder.length()>0) {
                numStack.push(new BigDecimal(numBuilder.toString()));
            }
            directCal(optstack, numStack, false);
            return numStack.pop().toString();
            
            
        }
        
        //带xml参数的计算
        public static String Calc(String expression,String xml) throws MyException {
            
            //判断表达式是否为空
            if(expression==null||expression.equals("")) {
                throw new IllegalArgumentException("表达式不能为空!");
            }
            
            Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
            if(!matcher.matches()){
                throw new IllegalArgumentException("表达式含有非法字符!");
            }
            
            expression = formatExpression(expression);
            isExpression(expression);
            
            Option option=JaxbUtil.convertToJavaBean(xml, Option.class);
            int op_num = 0;
            
            Stack<String> optstack = new Stack<>();
            Stack<BigDecimal> numStack = new Stack<>();
            StringBuilder numBuilder = new StringBuilder(16);
            
            for(int i=0;i<expression.length();i++) {
                char ch = expression.charAt(i);
                if(ch!=' ') {
                    if((ch>='0'&&ch<='9')||ch=='.') {
                        numBuilder.append(ch);
                    }else {
                        if(++op_num>option.getOperation_num())
                            throw new MyException("操作符个数超过设置最大个数!");
                        
                        if(numBuilder.length()>0) {
                            BigDecimal value = new BigDecimal(numBuilder.toString());
                            if(value.doubleValue()>option.getMax()||value.doubleValue()<option.getMin())
                                throw new MyException("数据超出设置范围!");
                            if(isNum(numBuilder.toString())==false) {
                                throw new MyException("表达式格式错误!————数字格式错误(错误数字:'"+numBuilder+"'");
                            }
                            numStack.push(value);
                            numBuilder.delete(0, numBuilder.length());
                        }
                        String operation = String.valueOf(ch);
                        if(optstack.empty()) {
                            optstack.push(operation);
                        }else {
                            if(operation.equals("(")) {
                                optstack.push(operation);
                            }else if(operation.equals(")")) {
                                directCal(optstack, numStack,true);
                            }else if(operation.equals("=")) {
                                directCal(optstack, numStack,false);
                                return String.format("%."+option.getPoint_long()+"f", numStack.pop());
                            }else {
                                compareOpt_Cal(numStack, optstack, operation);
                            }
                        }
                    }
                }
            }
            
            if(numBuilder.length()>0) {
                numStack.push(new BigDecimal(numBuilder.toString()));
            }
            directCal(optstack, numStack, false);
            return String.format("%."+option.getPoint_long()+"f", numStack.pop());
            
            
        }
        
        
        private static void compareOpt_Cal(Stack<BigDecimal> numstack,Stack<String> optstack,String operation) throws MyException {
            String topOpt = optstack.peek();
            int comp_val = getPriority(topOpt, operation);
            if(comp_val==-1||comp_val==0) {
                String opt = optstack.pop();
                BigDecimal num2 = numstack.pop();
                BigDecimal num1 = numstack.pop();
                BigDecimal result = calDouble(opt, num1, num2);
                numstack.push(result);
                if(optstack.empty()) {
                    optstack.push(operation);
                }else {
                    compareOpt_Cal(numstack, optstack, operation);
                }
            }else {
                optstack.push(operation);
            }
        }
        
        //判断两个操作符的优先级
        private static int getPriority(String op1,String op2) {
            return OP_PRIORITY_MAP.get(op2)-OP_PRIORITY_MAP.get(op1);
        }
        
        //两个数之间的运算,注意除数不能为0
        private static BigDecimal calDouble(String operation,BigDecimal num1,BigDecimal num2) throws MyException {
            BigDecimal result = new BigDecimal(0);
            switch (operation) {
            case "+":
                result = num1.add(num2);
                break;
            case "-":
                result = num1.subtract(num2);
                break;
            case "*":
                result = num1.multiply(num2);
                break;
            case "/":
                if(num2.equals(BigDecimal.ZERO)) {
                    throw new MyException("表达式格式错误!————数字格式错误(除数不能为0:'"+num1+"/"+num2+"')");
                }
                result = num1.divide(num2,10,BigDecimal.ROUND_DOWN);
                break;
            default:
                break;
            }
            return result;
        }
        
        //递归计算
        private static void directCal(Stack<String> opStack,Stack<BigDecimal> numStack,boolean haved) throws MyException {
            String opt = opStack.pop();
            BigDecimal num2 = numStack.pop();
            BigDecimal num1 = numStack.pop();
            BigDecimal result = calDouble(opt, num1, num2);
            
            numStack.push(result);
            
            if(haved) {
                if("(".equals(opStack.peek())) {
                    opStack.pop();
                }else {
                    directCal(opStack, numStack,haved);
                }
            }else {
                if(!opStack.empty()) {
                    directCal(opStack, numStack, haved);
                }
            }
        }
        
        //判断表达式的合法性(字符合法性)
        public static void isExpression(String expression) throws MyException {
            char pre=' ',cur=' ';
            int l_bracket_num=0,r_bracket_num=0;
            for(int i=0;i<expression.length();i++) {
                cur=expression.charAt(i);
                if(cur=='(') {
                    l_bracket_num++;
                }else if(cur==')') {
                    r_bracket_num++;
                }
                if(i>0) {
                    if(pre=='.'&&(cur=='.'||cur=='('||cur==')'||cur=='+'||cur=='-'||cur=='*'||cur=='/')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='.'||pre=='('||pre==')'||pre=='+'||pre=='-'||pre=='*'||pre=='/')&&cur=='.') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((cur=='.'||cur==')'||cur=='+'||cur=='-'||cur=='*'||cur=='/')&&pre=='(') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='.'||pre=='('||pre=='+'||pre=='-'||pre=='*'||pre=='/')&&cur==')') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='+'||pre=='-'||pre=='*'||pre=='/')&&(cur=='+'||cur=='-'||cur=='*'||cur=='/')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='='&&i!=expression.length()-1) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='='&&(pre=='+'||pre=='-'||pre=='*'||pre=='/'||pre=='(')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='('&&(pre>='0'&&pre<='9')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(pre==')'&&(cur>='0'&&cur<='9')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }
                }else {
                    if(cur=='/'||cur=='*'||cur=='.'||cur==')'||cur=='=')
                        throw new MyException("表达式格式错误!————表达式首字符不符合规范(错误字符:'"+cur+"'");
                }
                
                pre=cur;
            }
            if(l_bracket_num!=r_bracket_num)
                throw new MyException("表达式格式不正确!————存在不匹配的左右括号");
        }
        
        //判断表达式中数字的合法性
        public static boolean isNum(String num) {
            if(num.contains(".")) {
                for(int i=0;i<num.indexOf('.');i++){
                    if(i==0&&num.charAt(0)=='0'&&num.indexOf('.')>1) {
                        return false;
                    }
                }
            }else {
                if(num.charAt(0)=='0'&&num.length()>1)
                    return false;
            }
            return true;
        }
        
        //判断表达式的开头是否为+或-,如果是,则在其前面加上0方便操作
        public static String formatExpression(String expression) {
            String result="";
            if(expression.charAt(0)=='+'||expression.charAt(0)=='-')
                expression = "0"+expression;
            result = expression.replaceAll("[(]-", "(0-");
            result = result.replaceAll("[(][+]", "(0+");
            return result;
        }
        
        public static void main(String[] args) throws MyException {
            Option option = new Option();
            //设置最大字符数
            option.setOperation_num(10);
            //设置数值范围的最小值
            option.setMin(-1000);
            //设置数值范围的最大值
            option.setMax(1000);
            //设置结果保留的小数位
            option.setPoint_long(2);
            
            String xml = JaxbUtil.convertTomXml(option);
            
            //传入带xml参数的计算方法(如不需要直接去掉该参数即可)
            System.out.println(Calc("12+87",xml));
        }
    }
    package com.core.bean;
    
    public class MyException extends Exception{
        /**
         * 
         */
        private static final long serialVersionUID = 4709550237708038191L;
        public MyException() {}
        public MyException(String message) {super(message);}
    }
    package com.core.bean;
    
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class Option {
        private int operation_num;
        private double min;
        private double max;
        private int point_long;
        public int getOperation_num() {
            return operation_num;
        }
        public void setOperation_num(int operation_num) {
            this.operation_num = operation_num;
        }
        public double getMin() {
            return min;
        }
        public void setMin(double min) {
            this.min = min;
        }
        public double getMax() {
            return max;
        }
        public void setMax(double max) {
            this.max = max;
        }
        public int getPoint_long() {
            return point_long;
        }
        public void setPoint_long(int point_long) {
            this.point_long = point_long;
        }
        
    }
    package com.core.util;
    
    import java.io.StringReader;
    import java.io.StringWriter;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    import com.core.bean.Option;
    
    public class JaxbUtil {
        
        //javabean转换成xml
        private static String converToXml(Object obj,String encoding) {
            String result = null;
            try {
                JAXBContext context = JAXBContext.newInstance(obj.getClass());
                Marshaller marshaller = context.createMarshaller();
                //是否格式化xml(按标签自动换行)
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                //设置编码方式
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
                StringWriter writer = new StringWriter();
                marshaller.marshal(obj, writer);
                result = writer.toString();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            return result;
        }
        
        public static String convertTomXml(Object object) {
            return converToXml(object, "UTF-8");
        }
        
        //xml转换成javabean
        @SuppressWarnings("unchecked")
        public static<T> T convertToJavaBean(String xml,Class<T> c) {
            T t = null;
            try {
                JAXBContext context = JAXBContext.newInstance(c);
                Unmarshaller unmarshaller = context.createUnmarshaller();
                t = (T)unmarshaller.unmarshal(new StringReader(xml));
            }catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            
            return t;
        }
        
        public static void main(String[] args) {
            Option option = new Option();
            option.setOperation_num(4);
            option.setMin(-1000);
            option.setMax(1000);
            option.setPoint_long(2);
    //        System.out.println(convertTomXml(option));
            String xml = convertTomXml(option);
            
            Option option2=convertToJavaBean(xml, Option.class);
            System.out.println(option2.getMin()+"~"+option2.getMax());
        }
    }
  • 相关阅读:
    spark hbase
    Benchmark简介
    Flink的安装配置
    Hive入门及常用指令
    hadoop+yarn+hbase+storm+kafka+spark+zookeeper)高可用集群详细配置
    Linux最常用的命名
    数据库的零散的总结
    DBA总结
    MySQL主从架构配置
    mysql主从读写分离,分库分表
  • 原文地址:https://www.cnblogs.com/yangqqq/p/13070908.html
Copyright © 2011-2022 走看看