20194589 自动生成四则运算题第一版报告

需求分析

使用Java 语言完成一个自动生成四则运算试题的程序,一个命令行软件,启动之后选择需要生成的试题类型,然后在一个循环中生成这些试题。

功能设计

  • 基本功能
  1. 自动生成10道100以内的2个操作数的四则运算,包括加减乘除,运算的结果在100以内
  2. 删除重复模式 。
  3. 可以控制是否生成乘法和除法 。
  4. 可以控制数字范围,相关参数可以控制。
  5. 运算的数字可以为负数 。
  6. 生成的运算题目存储到外部文件result.txt 中 。
  • 扩展功能
  1. 题目生成后进入下一步,可以选择继续生成或者重新选择模式来生成。
  2. 生成的题目可以有多个数值参与运算 。
  3. 生成的结果可以包含负数 。
  4. 除法试题结果显示为余数形式,还是小数形式。
  5. 选择是否输出带有答案的题目。

设计

客户端只用来处理与用户的交互,获取随机数,计算数据等功能都通过其他类来实现

  1. MathFunction 设置一个模型类,用来表示一个等式,包含有等式的数据,计算出来结果也要添加到MathFunction 中的 result 中。获取结果的函数 getResult 函数使用装饰者模式实现。
  2. MathValue 这是一个模型类,为了完成多个值参与运算,需要使用 tree 数据模型来表示等式。
  3. RandomCreator 只是一个类,使用单例模式,通过 getRandom 方法获取一个随机数。
  4. Calculator 计算类的接口,用来计算等式结果,因为需要判断最后的结果是否符合,而且还要展示计算的答案,计算类必不可少。
  5. CalculatFactory 工厂类,用于根据客户端提供的函数,选择相应的Calculator 来计算结果。
  6. AddCalculator 继承自 Calculator 实现加法运算。
  7. SubCalculator 继承自 Calculator 实现减法运算。
  8. MultiCalculator 继承自 Calculator 实现乘法运算。
  9. DivCalculator 继承自 Calculator 实现除法运算。
  10. MathMaster 用于管理生成函数的关键类。需要计算10个算式,那么就需要使用 while 或者 for 循环,循环的内容也移动到这个类中,计算出来的算式保存到这个类中的字段mathFunctions。通过客户端的设定生成相应的算式,并通过客户端调用这里的函数 printFunctions 打印出来。
  11. MathElement MathFunctionMathValue 都继承自MathElement,拥有getValue 函数,并由MathFunctionMathValue 实现。

    模型相关类图


    简单工厂类图

测试运行

普通的运行一遍

  • 全部使用默认选项,实现默认功能
  • 删除重复项目
  • 输出结果

展示扩展功能

  • 实现扩展功能中的使用当前配置再次生成不同的题目
  • 可以继续选择配置,或者退出功能

展示参数可以控制功能

  • 可以控制试题数量
  • 控制题目类型,如乘除法
  • 负数参与运算

展示产生负数的结果

  • 结果可以为负数选项

粘贴代码

  1. MathFunction 中获取结果的方法

    public MathFunction(MathElement leftElement, int opeator, MathElement rightElement) {
        super();
        this.leftElement = leftElement;
        this.opeator = opeator;
        this.rightElement = rightElement;
        calculator = CalculatorFactory.getCalculator(opeator);
        op = calculator.getOpeator();
    }
    
    public int getOpeator() {
        return opeator;
    }
    
    @Override
    public int getValue() {
        return calculator.calc(leftElement.getValue(), rightElement.getValue());
    }
    
    public String getOutput(boolean result) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(leftElement.getValue() + " " + op + " " + rightElement.getValue());
        if (result) {
            stringBuilder.append(" = " + getValue());
        }
        return stringBuilder.toString();
    }
  2. CalculatorFactory 中获得相应的类的方法

    public static Calculator getCalculator(int op) {
        switch (op) {
        case 0:
            return new AddCalculator();
        case 1:
            return new SubCalculator();
        case 2:
            return new MultiCalculator();
        case 3:
            return new DivCalculator();
        default:
            throw new IllegalArgumentException("Unexpected value: " + op);
        }
    }
  3. RandomCreator 类,使用单例模式

    public class RandomCreator {
        private static RandomCreator creator;
        private Random random;
        private RandomCreator() {
            random=new Random(System.currentTimeMillis());
        }
        public int getRandom(int range) {
            return random.nextInt(range+1);
        }
        public static RandomCreator getCreator() {
            if (creator == null) {
                creator=new RandomCreator();
            }
            return creator;
    
        }
    }
  4. MathMaster 中获取试题的主要代码

    /**
     * 获取试题
     */
    public void get() {
        mathFunctions.clear();
        for(int functionIndex=0;functionIndex<onceCreateCount;) {
    
            int left=RandomCreator.getCreator().getRandom(onceCreateValueRange);
            if (whetherHasNegativeNumber) {
                if (RandomCreator.getCreator().getRandom(1)==0) {//0 就添加负号
                    left=0-left;
                }
            }
            int right=RandomCreator.getCreator().getRandom(onceCreateValueRange);
            if (whetherHasNegativeNumber) {
                if (RandomCreator.getCreator().getRandom(1)==0) {//0 就添加负号
                    right=0-right;
                }
            }
            int op=getRandom();//根据当前是否含有乘除法获取不同的运算符
            MathFunction mathFunction=new MathFunction(new MathValue(left),op,new MathValue(right));
            int result=mathFunction.getValue();
            if (checkResult(result)) {//如果结果不符合,忽略这次结果
                mathFunctions.add(mathFunction);
                functionIndex++;
            }//忽略的结果不会增加
        }
    }
  5. 客户端中用于控制是否按照当前配置重新生成

    String getInputString = scanner.next();
    char charAt = getInputString.charAt(0);
    MathMaster master = new MathMaster();
    while (!needExit(charAt)) {
        //···省略部分代码
        boolean exit=false;//退出下面的while 时是否还有退出整体的while
        while (true) {
            master.get();
            master.printFunctions(false);
                        FileWriter fileWriter=new FileWriter("result.txt");//打印到文件中
            fileWriter.write(master.getFuncString());
            fileWriter.flush();
            fileWriter.close();
            System.out.println("是否需要输出带有结果的题目,Y/e");
            getInputString = scanner.next();
            charAt = getInputString.charAt(0);
            if (needExit(charAt)) {
                exit=true;
                break;
            } else if (whetherCouldGoon(charAt)) {
                if (charAt == 'y' || charAt == 'Y') {
                    master.printFunctions(true);
                }
                System.out.println("重新开始,还是再生成一遍,Y/n/e");
                getInputString = scanner.next();
                charAt = getInputString.charAt(0);
                if (needExit(charAt)) {
                                        exit=true;
                    break;
                } else if (whetherCouldGoon(charAt)) {
                    if (charAt == 'y' || charAt == 'Y') {
                        //不做任何改变就可以在输出
                    }else {
                        break;//重新开始
                    }
                }
            } else {
                System.out.println(input_error);//重新开始
                break;
            }
        }
        if (exit) {
            break;
        }
            //···省略部分代码
    }
  6. AddCalculator 类的代码,其他运算与此类似

    public class AddCalculator implements Calculator {
    
        @Override
        public int calc(int left, int right) {
            return left+right;
        }
        /**
         * 从这里获取操作符,就不需要判断0 1 2 3 对应的+ - * (/) 了
         */
        @Override
        public char getOpeator() {
            return '+';
        }
    
    }

总结

  • 想要达成模块化最首选的就是函数,把一部分操作放到函数中,便于理解程序的运行,相同的操作还能复用代码,就像代码中的needExitwhetherCouldGoon 一样。
  • 再进一步就是类,遵守“知识最少原则” 当前的类不需要了解的操作或者实体,那就把它提取到其他的类中,然后将相应的操作都放到这个类中,一是可以减少一个类的大小,也减少了模块间的依赖,当修改一部分代码时,只需要修改当前类就可以,不需要动其他的类,其他的类也不需要参与重新编译的过程了。
  • 运用设计模式。在这个项目中使用了“单例模式”和“简单工程模式”,使用了单例模式,MathMaster 就不需要管理随机数的生成。使用了简单工厂模式,这样MathMaster 只需要获取答案,而不需要管理当前执行的是什么操作,顺利进行解耦。
  • 灵活运用接口和超类,达到代码复用,多态,这也是设计模式的基础。
  • 用面向对象的思想,万事万物皆是对象。
  • 单一对象原则,每个类只需要完成自己的操作就好了,如果一个类使用了很多的类,那就要考虑是不是要把这些内容都放到其他类中,然后从这里调用,而不是让一个类做太多的工作。

PSP

PSP2.1 计划任务 计划共完成需要的时间(min) 实际完成需要的时间(min)
Planning 计划 10 5
Estimate 估计这个任务需要多少时间,并规划大致工作步骤 10 10
Development 开发 95 253
Analysis 需求分析(包括学习新技术) 10 8
Design Spec 生成设计文档 10 10
Design Review 设计复审(和同事审核设计文档) 10 10
Coding Standard 代码规范(为目前的开发制定合适的规范) 5 5
Design 具体设计 10 20
Coding 具体编码 30 160
Code Review 代码复审 10 10
Test 测试(自我测试,修改代码,提交代码) 20 30
Reporting 报告 25 25
Test Report 测试报告 15 10
Size Measurement 计算工作量 5 10
Postmortem & Process Improvement Plan 事后总结,并提交过程改进计划 5 5

猜你喜欢

转载自www.cnblogs.com/fuzhengyin/p/11519900.html