• 结对编程(JAVA实现)


    项目成员:黄思扬(3117004657)、刘嘉媚(3217004685)

    一、github地址:https://github.com/Jasminejiamei/pairProgramming

    二、PSP表格

    PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
    Planning 计划  60 40
    · Estimate · 估计这个任务需要多少时间   60  40
    Development 开发 1440 1505
    · Analysis · 需求分析  30  15
    · Design Spec · 生成设计文档  20  15
    · Design Review · 设计复审   30  15
    · Coding Standard · 代码规范  20  20
    · Design · 具体设计  80  80
    · Coding · 具体编码  900  980
    · Code Review · 代码复审  30  30
    · Test · 测试(自我测试,修改代码,提交修改)  330  400
    Reporting 报告 130 100
    · Test Report · 测试报告  80  60
    · Size Measurement · 计算工作量  30  20
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划  30  20
      合计  1630   1695

    三、效能分析

      由于之前采用单线程执行,在文件IO流的处理上花费了不少的时间,包括代码上的执行存在部分冗余,代码上可以提高利用率。打开了线程池以后,多线程执行,大大提高了执行速度,在代码逻辑改进优化后,对大量生成题目的效果十分显著,由30s时间完成优化到2s: 

     

    四、设计过程

    (一)流程图

    视图设计过程:

    生成题目设计过程:

    判断对错设计过程:

    (二)项目目录:

    分包思路:

    1)视图层:view 包含主页面与两个功能页面

    2)实体类:po  包含题目存放类Deposit、ChildDeposit,分数处理类

    3)逻辑处理层:service  处理题目生成等的逻辑、处理题目判错的逻辑

    4)工具类:util 包含文件的读写功能

     

    (三)总体实现思路

      程序运行,进入主页面,点击选择进入相应功能页面(生成题目or判断对错),如果为生成题目,用户需要输入相关参数(题目数量、数的大小范围),视图层获取输入的数据,传入至逻辑层中进行处理,先判断输入是否有误,有误则终止程序,无误则调用题目生成的方法CreateAth;如果为判断对错,用户需要选择相应的文件(Exersises.txt和Answers.txt),视图层获取输入的数据,传入到逻辑层进行处理,判断输入无误后,调用题目判错的方法Judge。

      题目生成的思路:题目要求生成的算术运算符少于3个,先随机生成一个运算符的个数,传入到CreateAth方法中,先生成一个根结点即算术运算符,然后随机生成在左右子树小于总运算符个数的运算符个数,同时生成运算符,当生成运算符个数为0,则生成叶子结点即运算操作数,左右子树也是重复上述过程。把生成的算式放在一个动态数组里面,每当生成一个算式,就去检查里面是否包含这个算式,如果重复就去掉,达到查重的目的。

      判断对错的思路:使用readfile读取Exersises.txt文件内容,使用正则表达式分割开题目的序号和算式,算式是中缀表达式的表示方式,通过build方法把算式改为前缀表达式,如:1 + (( 2 + 3)* 4 ) – 5,转换成前缀则为- + 1 * + 2 3 4 5,计算其答案,读取Answers.txt的内容,使用正则表达式分割开题目的序号和答案,根据两个文件的序号,对比答案是否相同,如果相同则记录对的题目的数量,和题目序号,写出到Correction.txt文件。

     

    (四)细节实现思路

    1)如何保证基本的数值运算,确定参数的范围?

      自然数的数值之间的运算可简单实现,但是自然数、真分数、带分数之间的运算之间的格式需要自己设计的,并且题目要求“如果存在形如e1÷ e2的子表达式,那么其结果应是真分数”,经过讨论之后,决定把所有数据统一当成分数来处理,整数的分母则为1,在运算的过程中把分子与分母独立出来分别操作加减乘除运算,到最后再进行约分等化简处理。

    2)怎么生成算式并且查重?

      生成的算式要求不能产生负数、生成的题目不能重复,且即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目,经过讨论,决定使用二叉树来实现,由于二叉树的运算次序是孩子结点的运算次序要优先于根结点的,所以使用非叶子节点存放运算符,叶子结点存放数值,可以解决前后符号之间的优先级别关系,解决括号的添加问题,当父母结点的算术符优先级高于右孩子结点的算术运算符时,左右都要加括号,当相等时,则右孩子树要加括号;出现了负数时,交换左右子树即可;此外,解决了查重的复杂度问题,开始的方案想采用遍历的方式来达到查重,现只需要判断两棵树是否相同即可。

     

    五、程序关键代码

    从页面获取到数据,进行处理:

    package service;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class EntryJudge{
        public boolean Entry(String[] args){
            //向主页面返回的运行成功与否的标志
            boolean tag = false;
            //判断用户输入是否正确
            if(args.length == 0 || args.length % 2 != 0)  {
                tag = false;
                return tag;
            }
            //取出参数
            Map<String, String> params = checkParams(args);
            //执行相应处理
            CreateAth opera = new CreateAth(params);
            Judge check =  new Judge(params);
            if(params.containsKey("-e")&&params.containsKey("-a")){
                check.Judge();
                tag = true;
                return tag;
            } else if(params.containsKey("-n") || params.containsKey("-r") || params.containsKey("-d")) {
                opera.createAth();
                tag = true;
                return tag;
            }
            return tag;
        }
        private Map<String, String> checkParams(String[] args) {
            Map<String, String> params = new HashMap<>();
            for (int i = 0; i < args.length; i = i + 2) {
                params.put(args[i], args[i+1]);
            }
            return params;
        }
    }
    EntryJudge

     

    题目生成逻辑处理:

    package service;
    
    import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;
    import po.Deposit;
    import po.Fraction;
    import util.FileUtil;
    import po.ChildDeposit;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Stack;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 生成题目类
     */
    public class CreateAth{
        private int maxNum = 100; //生成题目的整数最大值
        private int denArea = 20; //分母的范围
        private int maxCount = 10;//生成题目数量
        private Deposit content;
        private static final String[] SYMBOLS = new String[]{
                "+", "-", "x", "u00F7"
        };
    
        /**
         * 生成随机题目,初始化,把主类中输入的参数内容调进来
         */
        public CreateAth(Map<String, String> params) {
            for (String str : params.keySet()) {
                if (str.equals("-n")) {
                    maxCount = Integer.valueOf(params.get(str));
                } else if (str.equals("-r")) {
                    maxNum = Integer.valueOf(params.get(str));
                } else if (str.equals("-d")) {
                    denArea = Integer.valueOf(params.get(str));
                }
            }
        }
    
        /**
         * 生成题目
         */
        private ExecutorService executor = Executors.newCachedThreadPool();
    
        public void createAth() {
            StringBuilder exercises = new StringBuilder();
            StringBuilder answers = new StringBuilder();
            List<CreateAth> list = new ArrayList<>();
            long start = System.currentTimeMillis();
            for (int i = 1; i <= maxCount;) {
                CreateAth generate = new CreateAth(true);
                if (!list.contains(generate)){
                    String[] strs = generate.print().split("=");
                    exercises.append(i).append(". ").append(strs[0]).append("
    ");
                    answers.append(i).append(".").append(strs[1]).append("
    ");
                    list.add(generate);
                    i++;
                }
            }
            executor.execute(() -> FileUtil.writeFile(exercises.toString(), "Exercises.txt"));
            executor.execute(() -> FileUtil.writeFile(answers.toString(), "Answers.txt"));
            executor.shutdown();
            long end = System.currentTimeMillis();
            try {
                boolean loop = true;
                while (loop) {
                    loop = !executor.awaitTermination(30, TimeUnit.SECONDS);  //超时等待阻塞,直到线程池里所有任务结束
                } //等待所有任务完成
                System.out.println("生成的" + maxCount + "道题和答案存放在当前目录下的Exercises.txt和Answers.txt,耗时为:"+(end - start) + "ms");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        ——————以下是生成题目所调用到的方法———————————
        /**
         * 生成组成题目随机数
         * area:分母的范围
         */
        private int random(int area) {
            ThreadLocalRandom random = ThreadLocalRandom.current();
            int x = random.nextInt(area);
            if (x == 0) x = 1;
            return x;
        }
        /**
         * ThreadLocalRandom类在多线程环境中生成随机数。
         * nextBoolean() 方法用于从随机数生成器的序列返回下一个伪随机的,均匀分布的布尔值
         */
        private boolean randomBoolean() {
            ThreadLocalRandom random = ThreadLocalRandom.current();
            return random.nextBoolean();
        }
        /**
         * 把生成的每个数进行处理得出分子分母
         */
        private Fraction creator() {
            if (randomBoolean()) {
                return new Fraction((random(maxNum)), 1);
            } else {
                if (randomBoolean()) {
                    int den = random(denArea);
                    int mol = random(den * maxNum);
                    return new Fraction(den, mol);
                } else {
                    int den = random(denArea);
                    return new Fraction(random(den), den);
                }
            }
        }
    
        /**
         * 单步计算
         * @param symbol 符号
         * @param left  左
         * @param right 右
         * @return 得出来结果后经过约分的分数
         */
        private Fraction calculate(String symbol, Fraction left, Fraction right) {
            switch (symbol) {
                case "+":
                    return left.add(right);
                case "-":
                    return left.subtract(right);
                case "x":
                    return left.multiply(right);
                default:
                    return left.divide(right);
            }
        }
    
        /**
         * 随机生成一道四则运算题目
         * @param fractionNum 运算符个数
         * @return 二叉树
         */
        private Deposit build(int fractionNum){
            if(fractionNum == 0){
                return new Deposit(creator(),null,null);
            }
            ThreadLocalRandom random = ThreadLocalRandom.current();
            ChildDeposit node = new ChildDeposit(SYMBOLS [random.nextInt(4)],null, null);
            //左子树运算符数量
            int left = random.nextInt(fractionNum);
            //右子树运算符数量
            int right = fractionNum - left - 1;
            node.setLeft(build(left));
            node.setRight(build(right));
            Fraction value = calculate(node.getSymbol(),node.getLeft().getValue(),node.getRight().getValue());
            //负数处理
            if(value.Negative()){
                //交换左右子树,就是交换两个减数的顺序
                if (node != null) {
                    Deposit swap = node.getLeft();
                    node.setLeft(node.getRight());
                    node.setRight(swap);
                }
                value = calculate(node.getSymbol(),node.getLeft().getValue(),node.getRight().getValue());
            }
            node.setValue(value);
            return node;
        }
    
    
        /**
         * 获取表达式,
         * 打印题目与答案
         */
        private String print(){
            return print(content) +  " = " + content.getValue();
        }
    
        private String print(Deposit node){
            if (node == null){
                return "";
            }
            String frac = node.toString();
            String left = print(node.getLeft());
            if (node.getLeft() instanceof ChildDeposit && node instanceof ChildDeposit) {
                if (bracketsLeft(((ChildDeposit) node.getLeft()).getSymbol(), ((ChildDeposit) node).getSymbol())) {
                    left = "(" + " " + left + " " + ")";
                }
            }
            String right = print(node.getRight());
            if (node.getRight() instanceof ChildDeposit && node instanceof ChildDeposit) {
                if (bracketsRight(((ChildDeposit) node.getRight()).getSymbol(), ((ChildDeposit) node).getSymbol())) {
                    right = "(" + " " + right + " " + ")";
                }
            }
            return left + frac + right;
        }
        /**
         * 比较两个符号谁优先级更高,子树的箱号优先级低要加括号,左括号or右括号
         */
        private boolean bracketsLeft(String left,String mid){
            return (left.equals("+")|| left.equals("-")) && (mid.equals("x")||mid.equals("u00F7"));
        }
        private boolean bracketsRight(String right, String mid){
            return (right.equals("+")|| right.equals("-")) && (mid.equals("x")||mid.equals("u00F7"))||(mid.equals("u00F7"))||(mid.equals("-")&&(mid.equals("+")|| mid.equals("-")));
        }
    
        /**
         *生成一个题目,先调用下面的createAth方法来判断有没有生成一个用build方法生成的树
         */
        CreateAth(boolean isBuild){
            if(isBuild){
                ThreadLocalRandom random = ThreadLocalRandom.current();
                int kind = random.nextInt(4);
                if (kind == 0) kind = 1;
                content = build(kind);
                while (content.getValue().Zero()){
                    content =build(kind);
                }
            }
        }
    
        /**
         * 查重
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof CreateAth)) return false;
            CreateAth exercise = (CreateAth) o;
            return content.equals(exercise.content);
        }
    
        Fraction getResult() {
            return content.getValue();
        }
    
        ——————以下是判断题目所要调用到的方法———————————
        /**
         * 中缀表达式生成树,用栈的特点,把中缀表达式变成前缀表达式
         * 在判错中调用
         * @param exercise 中缀表达式
         * @return 二叉树
         */
        Deposit build(String exercise) {
            String[] strs = exercise.trim().split(" "); //拿走标号
            Stack<Deposit> depositStack = new Stack<>();  //结点栈
            StringStack symbolStack = new StringStack();  //符号栈
            //中缀表达式转换成前缀表达式,然后再用前序遍历生成数
            for (int i = strs.length - 1; i >= 0; i--) {
                String str = strs[i];
                if (!str.matches("[()+\u00F7\-x]")) {
                    depositStack.push(new Deposit(new Fraction(str)));
                } else {
                    //符号结点
                    while (!symbolStack.empty() && ((symbolStack.peekString().equals("x") ||
                            symbolStack.peekString().equals("u00F7"))
                            && (str.equals("+") || str.equals("-"))
                            || str.equals("("))) {
                        String symbol = symbolStack.popString();
                        if (symbol.equals(")")) {
                            break;
                        }
                        push(symbol, depositStack);
                    }
                    if (str.equals("(")) {
                        continue;
                    }
                    symbolStack.pushString(str);
                }
            }
            while (!symbolStack.empty()) {
                push(symbolStack.popString(), depositStack);
            }
            this.content = depositStack.pop();
            return content;
        }
    
        /**
         * 将符号压入节点栈且计算结果,仅在生成前缀表达式
         */
        private void push(String symbol, Stack<Deposit> nodeStack) {
            Deposit left = nodeStack.pop();
            Deposit right = nodeStack.pop();
            ChildDeposit node = new ChildDeposit(symbol, left, right);
            node.setValue(calculate(symbol, left.getValue(), right.getValue()));
            nodeStack.push(node);
        }
    }
    CreateAth

    题目判错处理:判断答案的正确性,并记录下来正确题目与错误题目序号,打印到Grade.txt

    package service;
    
    import po.Fraction;
    import util.FileUtil;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 答案判错类
     */
    public class Judge{
        private int trueNum;  //正确数目
        private int wrongNum;    //错误数目
        private String exerciseFileName;  // 题目文件名
        private String answerFileName;  // 答案文件名
    
        public Judge(Map<String, String> params) {
            for (String str : params.keySet()) {
                if (str.equals("-e")) {
                    exerciseFileName = params.get(str);
                } else if (str.equals("-a")) {
                    answerFileName = params.get(str);
                }
            }
        }
        /**
         * 判断错误 ,并把错误写入文件
         */
        public void Judge() {
            long start = System.currentTimeMillis();
            List<String> correctNums = new ArrayList<>();
            List<String> wrongNums = new ArrayList<>();
            FileUtil.readFile((exercise, answer) -> {
                String[] strs1 = exercise.split("\."); //匹配每一行
                String[] strs2 = answer.split("\.");
                if (strs1[0].equals(strs2[0])) {
                    CreateAth exes = new CreateAth(false);
                    exes.build(strs1[1].trim());  //去掉两端的空格后,将后缀表达式生成树变成前缀的,
                    if (exes.getResult().equals(new Fraction(strs2[1].trim()))) {   //答案两边都相等,继续执行下面的
                        correctNums.add(strs1[0]);
                        trueNum++;
                    } else {
                        wrongNums.add(strs1[0]);
                        wrongNum++;
                    }
                }
            }, exerciseFileName, answerFileName);
            FileUtil.writeFile(printResult(correctNums, wrongNums), "Correction.txt");
            long end = System.currentTimeMillis();
            System.out.println("题目答案对错统计存在当前目录下的Correction.txt文件下,耗时为:" + (end - start) + "ms");
        }
    
        private String printResult(List<String> correctNums, List<String> wrongNums) {
            StringBuilder builder = new StringBuilder();
            builder.append("Correct: ").append(trueNum).append(" (");
            for (int i = 0; i < correctNums.size(); i++) {
                if (i == correctNums.size() - 1) {
                    builder.append(correctNums.get(i));
                    break;
                }
                builder.append(correctNums.get(i)).append(", ");
            }
            builder.append(")").append("
    ");
            builder.append("Wrong: ").append(wrongNum).append(" (");
            for (int i = 0; i < wrongNums.size(); i++) {
                if (i == wrongNums.size() - 1) {
                    builder.append(wrongNums.get(i));
                    break;
                }
                builder.append(wrongNums.get(i)).append(", ");
            }
            builder.append(")").append("
    ");
            return builder.toString();
        }
    }
    Judge

    分数处理类:把所有随机生成的数都当成是分数处理,同时在些定义分数的四则运算方法

    package po;
    
    /**
     * 1. 把所有随机生成的数都当成是分数处理(解决了自然整数,分数,带分数之间的差异)
     * 2. 定义了分数的四则运算类
     */
    public class Fraction{
    
        private int mol; //分子
        private int den;  //分母
    
        /**
         * 处理随机生成的数值(约分等),组合分数对象
         */
        public Fraction(int mol, int den) {
            this.mol = mol;
            this.den = den;
            if (den <= 0) {
                throw new RuntimeException("分数分母不能为0");
            }
            //否则就进行约分
            int mod = 1;
            int max = den > mol ? den : mol;
            for (int i = 1; i <= max; i++) {
                if (mol % i == 0 && den % i == 0) {
                    mod = i;
                }
            }
            this.mol = mol / mod;
            this.den = den / mod;
        }
    
        /**
         * 处理随机生成的数值,这个用于分解分数对象(仅在判错中使用)
         */
        public Fraction(String str) {
            int a = str.indexOf("'");
            int b = str.indexOf("/");
            if (a != -1) {
                //取出数组,转换类型
                int c = Integer.valueOf(str.substring(0, a));
                den = Integer.valueOf(str.substring(b + 1));
                mol = c * den + Integer.valueOf(str.substring(a + 1, b));
            } else if (b != -1) {
                String[] sirs = str.split("/");
                mol = Integer.valueOf(sirs[0]);
                den = Integer.valueOf(sirs[1]);
            } else {
                mol = Integer.valueOf(str);
                den = 1;
            }
        }/**
         * 定义加减乘除类,返回值类型(全都当成分数处理),由于要返回这个类的内容,所以方法前要加类名
         */
        public Fraction add(Fraction fraction) {
            return new Fraction(this.mol * fraction.den + this.den * fraction.mol, this.den * fraction.den);
        }
    
        public Fraction subtract(Fraction fraction) {
            return new Fraction(this.mol * fraction.den - this.den * fraction.mol, this.den * fraction.den);
        }
    
        public Fraction multiply(Fraction fraction) {
            return new Fraction(this.mol * fraction.mol, this.den * fraction.den);
        }
    
        public Fraction divide(Fraction fraction) {
            return new Fraction(this.mol * fraction.den, this.den * fraction.mol);
        }
    
    }
    Fraction

     

    题目实现存放类以及其子类:

    package po;
    
    import java.util.Objects;
    /**
     * 用于存放题目的类,用二叉树的形式
     */
    public class Deposit{
    
        private Deposit left;
        private Deposit right;
        private Fraction value; //用于二叉树结点的是符号与运算结果数值的之间的变化
    
        public Deposit(Fraction value, Deposit left, Deposit right){
            this.value = value;
            this.left = left;
            this.right = right;
        }
    
        /**
         * 取结点数据
         */
        public Fraction getValue() {
            return value;
        }
        public Deposit getRight(){
            return right;
        }
        public Deposit getLeft(){
            return left;
        }
    
        /**
         * 设置结点数据
         */
        public Deposit(Fraction value){
            this.value = value;
        }
        public void setLeft(Deposit left){
            this.left = left;
        }
        public void setRight(Deposit right){
            this.right = right ;
        }
        public void setValue(Fraction value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return value.toString();
        }
    
        /**
         * 用于查重,判断二棵树是否相同
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Deposit)) return false;
            Deposit node = (Deposit) o;
            return Objects.equals(value, node.value) &&
                    Objects.equals(left, node.left) &&
                    Objects.equals(right, node.right);
        }
    }
    
    —————————————————————————————————————分割线——————————————————————————————————————————————
    
    package po;
    
    /**
     * 用于记录符号结点,与Deposit类是一样的道理
     */
    public class ChildDeposit extends Deposit{
    
        private String symbol;
    
        public ChildDeposit(String symbol, Deposit left, Deposit right){
            super(null, left, right);
            this.symbol = symbol;
        }
    
        public String getSymbol() {
            return symbol;
        }
    
        @Override
        public String toString() {
            return " " + symbol + " ";
        }
    
        /**
         * 用于查重
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof ChildDeposit)) return false;
            ChildDeposit that = (ChildDeposit) o;
    
            boolean flag = this.symbol != null && symbol.equals(that.symbol);
            if(!flag) return false;
    
            boolean left = this.getLeft() != null && getLeft().equals(that.getLeft());
            boolean right = this.getRight() != null && getRight().equals(that.getRight());
            //左右子树相同
            if(left && right) {
                return true;
            }
            if(left ^ right) {
                return false;
            }
            //如果是加法或乘法由于满足交换律所以要判断
            if(this.symbol.equals("+") || this.symbol.equals("x")) {
                left = this.getLeft() != null && getLeft().equals(that.getRight());
                right = this.getRight() != null && getRight().equals(that.getLeft());
            }
            return left && right;
        }
    }
    Deposit,ChildDeposit

     

    文件读写工具类:处理文件的写入与读出

    package util;
    
    import java.io.*;
    
    public class FileUtil{
        /**
         * 写入文件中
         */
        public static void writeFile(String content, String fileName) {
            File file = new File(fileName);
            try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))){
                if(!file.exists()){
                    file.createNewFile();
                }
                bw.write(content);
                bw.flush();
            } catch (IOException e) {
                System.out.println("文件操作失败...");
            }
        }
        /**
         * 读文件内容
         * @param callBack 回调接口,分别处理每一行
         * @param exerciseFileName 题目文件
         * @param answerFileName 答案文件
         */
        public static void readFile(ReaderCallBack callBack, String exerciseFileName, String answerFileName) {
            File exerciseFile = new File(exerciseFileName);
            File answerFile = new File(answerFileName);
            if(!exerciseFile.exists() || !answerFile.exists()) {
                System.out.println("文件不存在!");
                return;
            }
            try (BufferedReader br1 = new BufferedReader(new FileReader(exerciseFileName));
                 BufferedReader br2 = new BufferedReader(new FileReader(answerFileName))) {
                String line1, line2;
                while ((line1 = br1.readLine()) != null && (line2 = br2.readLine()) != null) {
                    callBack.deal(line1, line2);
                }
            } catch (IOException e) {
                System.out.println("读取文件失败!");
            }
        }
    
        public interface ReaderCallBack {
            void deal(String exercise, String answer) throws IOException;
        }
    }
    FileUtil

    图形界面:

    MainView :主页面,用户在两种操作中选一进行

    package view;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class MainView {
        public static void main(String[] args) {
            new MainJFrame();
        }
    }
    class MainJFrame extends JFrame {
        /**
         *   程序主界面
         */
        private static final long serialVersionUID = 1;
        //定义全局变量
    
        private JLabel title;
        private JButton generate,judge;
    
        private JLabel result;
        private JPanel down = new JPanel();
    
        //创建一个容器
        Container ct;
    
        MainJFrame(){
    
            ct=this.getContentPane();
            this.setLayout(null);//设置容器为空布局,绝对定位
    
            //标题
            title= new JLabel("四则运算题目生成程序");
            title.setFont(new Font("微软雅黑",Font.BOLD, 30));
            title.setBounds(140, 40, 340, 100);
    
            //生成
            generate = new JButton("生成题目");
            generate.setBounds(120, 220, 140, 40);
            generate.addActionListener(new generateListener());
            //判错
            judge = new JButton("判断对错");
            judge.setBounds(300, 220, 140, 40);
            judge.addActionListener(new judgeListenner());
    //添加组件
    
            ct.add(title);
            ct.add(generate);
            ct.add(judge);
            ct.add(down);
            this.setTitle("MyApp");
            this.setSize(600, 450);// 设置窗口大小
            this.setLocationRelativeTo(null);//基本设置  把窗口位置设置到屏幕中心
            this.setVisible(true);//显示窗口
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  //关闭窗口 当点击窗口的关闭按钮时退出程序(没有这一句,程序不会退出)
        }
    
    
        class generateListener implements ActionListener {
            //监听生成按钮点击事件
            public void actionPerformed(ActionEvent e) {
    
                new GenerateView();
            }
    
        }
        class judgeListenner implements ActionListener{
            //监听判错按钮点击事件
            public void actionPerformed(ActionEvent e) {
                new JudgeView();
    
            }
        }
    }
    MainView

     

    GenerateView:用户输入参数范围、生成题目的页面

    package view;
    
    import service.EntryJudge;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class GenerateView extends JFrame {
    
    
        private JLabel title,result;
        private JButton confirm;
        private JLabel subjectNum,intArea,denArea;
        private JTextField subjectNumField,intAreaField,denAreaField;
        private JPanel down = new JPanel();
    
    
        public GenerateView() {
            //创建一个容器
            Container ct;
            ct=this.getContentPane();
    //        this.setLayout(null);//设置容器为空布局,绝对定位
    
            this.setSize(600, 450);//基本设置
            this.setLocationRelativeTo(null);
            this.setLayout(null);
            title = new JLabel("生成题目");
            title.setFont(new Font("微软雅黑",Font.BOLD, 20));
            title.setBounds(200,20,280,60);
            //生成题目数
            subjectNum = new JLabel("请输入生成题目数:");
            subjectNum.setFont(new Font("微软雅黑",Font.BOLD, 16));
            subjectNum.setBounds(70,100,160,50);
            subjectNumField = new JTextField (10);
            subjectNumField.setBounds(260,100,260,40);
            //整数范围
            intArea = new JLabel("请输入整数范围:");
            intArea.setFont(new Font("微软雅黑",Font.BOLD, 16));
            intArea.setBounds(70,150,160,50);
            intAreaField = new JTextField (20);
            intAreaField.setBounds(260,150,260,40);
            //分母范围
            denArea = new JLabel("请输入分数分母的范围:");
            denArea.setFont(new Font("微软雅黑",Font.BOLD, 16));
            denArea.setBounds(70,200,180,50);
            denAreaField = new JTextField (20);
            denAreaField.setBounds(260,200,260,40);
            confirm = new JButton("确定");
            confirm.setBounds(250,270, 60, 50);
            confirm.addActionListener(new ConfirmActionListener());
            //设置底部panel
            down.setBounds(130, 330, 280, 50);
            //设置底部panel背景透明
            down.setBackground(null);
            down.setOpaque(false);
            result= new JLabel();
            result.setFont(new Font("微软雅黑",Font.BOLD, 18));
            //添加组件
            down.add(result);
            ct.add(title);
            ct.add(subjectNum);
            ct.add(subjectNumField);
            ct.add(intArea);
            ct.add(intAreaField);
            ct.add(denArea);
            ct.add(denAreaField);
            ct.add(confirm);
            ct.add(down);
    
            this.setVisible(true);//显示窗口
            this.setLocationRelativeTo(null);//基本设置  把窗口位置设置到屏幕中心
            this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);  //关闭窗口
        }
        class ConfirmActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
    
                String args1 = subjectNumField.getText();
                String args2 = intAreaField.getText();
                String args3 = denAreaField.getText();
    
                String [] args  = new String[]{"-n",args1,"-r",args2,"-d",args3};
                EntryJudge ej = new EntryJudge();
                boolean res = ej.Entry(args);
                //获取命令执行结果
                if(res == true) {
                    result.setText("结果:生成题目成功" );
    
                } else{
                    result.setText("结果:生成题目失败" );
    
                }
            }
        }
    }
    GenerateView

    JudegeView:用户选择文件,是判断对错的页面

    package view;
    
    import service.EntryJudge;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class JudgeView extends JFrame {
    
    
        private JLabel title,result;
        private JButton confirm;
        private JLabel subjectNum,intArea,file1,file2;
        private JButton btn1,btn2;
        private JPanel down = new JPanel();
    
    
        public JudgeView() {
            //创建一个容器
            Container ct;
            ct=this.getContentPane();
    
            this.setSize(600, 450);//基本设置
            this.setLocationRelativeTo(null);
            this.setLayout(null);
            title = new JLabel("判断对错");
            title.setFont(new Font("微软雅黑",Font.BOLD, 20));
            title.setBounds(200,20,280,60);
            //生成题目数
            subjectNum = new JLabel("请选择题目文件:");
            subjectNum.setFont(new Font("微软雅黑",Font.BOLD, 16));
            subjectNum.setBounds(70,100,160,50);
            btn1 = new JButton ("选择文件");
            btn1.setBounds(240,100,120,40);
            btn1.addActionListener(new ChooseFile1ActionListener());
            file1 = new JLabel();
            file1.setFont(new Font("微软雅黑",Font.BOLD, 14));
            file1.setBounds(390,100,130,50);
            //整数范围
            intArea = new JLabel("请输入答案文件:");
            intArea.setFont(new Font("微软雅黑",Font.BOLD, 16));
            intArea.setBounds(70,150,160,50);
            btn2 = new JButton ("选择文件");
            btn2.setBounds(240,150,120,40);
            btn2.addActionListener(new ChooseFile2ActionListener());
    
            file2 = new JLabel();
            file2.setFont(new Font("微软雅黑",Font.BOLD, 14));
            file2.setBounds(390,150,130,50);
    
            confirm = new JButton("确定");
            confirm.setBounds(250,270, 60, 50);
            confirm.addActionListener(new ConfirmActionListener());
            //设置底部panel
            down.setBounds(130, 330, 280, 50);
            //设置底部panel背景透明
            down.setBackground(null);
            down.setOpaque(false);
            result= new JLabel();
            result.setFont(new Font("微软雅黑",Font.BOLD, 18));
            //添加组件
            down.add(result);
            ct.add(title);
            ct.add(subjectNum);
            ct.add(btn1);
            ct.add(file1);
            ct.add(intArea);
            ct.add(btn2);
            ct.add(file2);
    
            ct.add(confirm);
            ct.add(down);
    
            this.setVisible(true);//显示窗口
            this.setLocationRelativeTo(null);//基本设置  把窗口位置设置到屏幕中心
            this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);  //关闭窗口
        }
        class ChooseFile1ActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
    
                JFileChooser chooser = new JFileChooser();             //设置选择器
                chooser.setMultiSelectionEnabled(true);             //设为多选
                int returnVal = chooser.showDialog(new JLabel(),"选择");
                String filename = chooser.getSelectedFile().getName();      //获取绝对路径
    
                file1.setText(filename);
    
            }
        }
        class ChooseFile2ActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
    
                JFileChooser chooser2 = new JFileChooser();             //设置选择器
                chooser2.setMultiSelectionEnabled(true);             //设为多选
                int returnVal = chooser2.showDialog(new JLabel(),"选择");
    
                String filename = chooser2.getSelectedFile().getName();      //获取绝对路径
    
                file2.setText(filename);
            }
        }
        class ConfirmActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent e) {
                //获取结果
                String [] args = new String[]{"-e", "Exercises.txt","-a","Answers.txt"};
                EntryJudge ej = new EntryJudge();
                boolean res = ej.Entry(args);
                //获取命令执行结果
                if(res == true) {
                    result.setText("结果:生成题目成功" );
    
                } else{
                    result.setText("结果:生成题目失败" );
    
                }
            }
        }
    }
    JudegeView

    六、测试测试结果

    程序运行,成功打开图形界面:

     

    测试题目生成是否成功:

    输入相关参数,点击确定 ,生成题目成功:

     查看Exersises.txt,成功生成题目:

     

     查看Answers.txt,答案:

    测试判错程序:

    改错二道题的答案:

    打开判错的图形界面,选择算式文件、选择答案文件:

    点击确定,查看统计文件Grade.txt:

    生成一万道算式:

    详细的github地址:

    算式生成文件:https://github.com/Jasminejiamei/pairProgramming/blob/master/Exercises.txt

    答案生成文件:https://github.com/Jasminejiamei/pairProgramming/blob/master/Answers.txt

    部分截图:

     

    七、项目小结

          此次的结对编程,我们均涉及了代码的设计、编写以及测试。讨论项目的基本构思对我负责的前期编码十分重要,从基础数值的定义和生成,再到采用二叉树存放算式,最后一步步完成项目,通过两个人的讨论,使得项目的整体思路变得清晰。在后期的编码中我的伙伴对我的编程方式提出了更有条理性的建议,最终在小改动下让整个项目的代码变得更为整洁与条理。

         多讨论交流意见对编程的帮助很大,在判错功能中,从开始的没有头绪,到讨论出来通过应用一个栈的实例:中缀表达式转前缀表达式,解决了判错的问题;开始项目生成大数量题目的速度很慢,在小伙伴丰富的编程经验下,指出了关于多线程运行程序的方式,并且通过优化一些代码,大大提高了效率。

     
  • 相关阅读:
    BASE64Encoder/BASE64Decoder(转)
    对象转化为json
    Intent
    RecyclerView的单击和长按事件(转)
    selector的例子
    修改app工程名 Android Studio
    The number of method references in a .dex file cannot exceed 64K.(转)
    $(function() {})
    jQuery插件开发的两种方法及$.fn.extend的详解(转)
    myeclipse2014安装aptana3.4.0插件(转)
  • 原文地址:https://www.cnblogs.com/liujiamei/p/11666715.html
Copyright © 2020-2023  润新知