GitHub地址:https://github.com/iylong/sizeyunsuanlianxiruanjian

1、实验目的与要求

(1)练习结对编程(Pair programming),体验软件项目开发中的两人合作。

(2)掌握Github上增量发布软件的操作方法。

2.需求分析

 ( 1  ) 由计算机从题库文件中随机选择20道加减乘除混合算式,用户输入算式答案,程序检查答案是否正确,每道题正确计5分,错误不计分,20道题测试结束后给出测试总分;

(2)题库文件可采用实验二的方式自动生成,也可以手工编辑生成,文本格式如下:

(3)程序为用户提供三种进阶四则运算练习功能选择:百以内整数算式(必做)、带括号算式、真分数算式练习;

(4)程序允许用户进行多轮测试,提供用户多轮测试分数柱状图,示例如下:

(5)程序记录用户答题结果,当程序退出再启动的时候,可为用户显示最后一次测试的结果,并询问用户可否进行新一轮的测试;

(6)测试有计时功能,测试时动态显示用户开始答题后的消耗时间。

(7)程序人机交互界面是GUI界面(WEB页面、APP页面都可),界面支持中文简体(必做)/中文繁体/英语,用户可以进行语种选择。

3.性能分析

(1)用户登录功能

                要求用户输入用户名以及密码登录到答题页面。

(2)倒计时功能:

                倒计时提示用户答题时间,超出规定时间,视为答题错误,记为零分。

(3)答题记录及统计功能:

                程序记录用户答题结果,当程序退出再启动的时候,可为用户显示最后一次测试的结果,并询问用户可否进行新一轮的测试。

(4)多次运行功能:

                程序允许用户进行多轮测试,提供用户多轮测试分数柱状图 

4.软件设计

   

5.核心功能代码展示

(1)用户登录        

import javax.swing.*;
import client.Client;
import form.form;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Login {

    public static void login() {

        JFrame f = new JFrame();

        f.setTitle("系统登录界面");

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 设置窗口的大小和位置
        f.setSize(600, 400);
        f.setLocation(420, 500);

        Container con = f.getContentPane();// 生成一个容器

        con.setLayout(new GridLayout(4,0));
        // 生成一个新的版面
        JPanel pan1 = new JPanel();

        JLabel title = new JLabel("第一届小学生数学竞赛系统");
        title.setFont(new Font("宋体", Font.BOLD, 25));
        pan1.add(title);

        JPanel pan2 = new JPanel();
        JLabel name = new JLabel("用户名:");
        name.setFont(new Font("宋体", Font.BOLD, 20));
        pan2.add(name);
        JTextField nameTest = new JTextField(15);
        pan2.add(nameTest);
        // 生成一个新的版面
        JPanel pan3 = new JPanel();
        JLabel pass = new JLabel("密  码:");
        pass.setFont(new Font("宋体", Font.BOLD, 20));
        pan3.add(pass);
        JPasswordField password = new JPasswordField(15);
        password.setEchoChar('*');
        pan3.add(password);

        con.add(pan1);
        con.add(pan2);
        con.add(pan3);

        JPanel pan4 = new JPanel();
        
        JButton b_log = new JButton("登陆");
        
        b_log.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                // 获取用户名和密码,进行校验
                String Username = nameTest.getText();
                String Password = password.getText();
                if (Username.equals("admin") && Password.equals("123456")) {
                    JOptionPane.showMessageDialog(null, "登陆成功!");
                    f.dispose();
                    form fm = new form();
                    fm.show();
                    
                } else {
                    JOptionPane.showMessageDialog(null, "账号或密码错误!");
                    nameTest.setText("");
                    password.setText("");
                }
            }
        });
        JButton b_exit = new JButton("退出");
        pan4.add(b_log);
        pan4.add(b_exit);
        con.add(pan4);
        f.setVisible(true);  
}

(2)生成计算式并且校对答案

public void createTest() {
        // 定义一个随机数
        Random random = new Random();
        /*
         * 定义两个个Fractions对象,分别为: 第一个运算数 第二个运算数
         */
        Fractions fractions_1 = new Fractions();
        Fractions fractions_2 = new Fractions();
        // 定义Caculate对象用于计算结果
        Caculate caculate = new Caculate();
        // 生成10道题
        for (int i = 0; i < testNum; i++) {
            // 分别随机 生成第一个和第二个运算数并储存到question字符串集里
            fractions_1.setValue(random.nextInt(diffculty) + 1, random.nextInt(diffculty) + 1);
            question_1[i] = fractions_1.getFraction();
            fractions_2.setValue(random.nextInt(diffculty) + 1, random.nextInt(diffculty) + 1);
            question_2[i] = fractions_2.getFraction();
            caculate.setOperationNum(fractions_1, fractions_2);
            // 随机生成符号并计算结果存储到result字符串集
            switch (random.nextInt(4)) {
            // 加法运算
            case 0:
                operator[i] = "+"; // 储存运算符
                result[i] = caculate.addtion().getFraction(); // 获取运算结果
                break;
            // 减法运算
            case 1:
                operator[i] = "-";
                result[i] = caculate.subtraction().getFraction();
                break;
            // 乘法运算
            case 2:
                operator[i] = "*";
                result[i] = caculate.multiplication().getFraction();
                break;
            // 除法运算
            case 3:
                operator[i] = "÷";
                result[i] = caculate.division().getFraction();
                break;
            }
        }
    }

    // 传入一组答案,校验答案
    public String[] checkAnswer(String[] answers) {
        for (int i = 0; i < testNum; i++) {
            // 如果答错了记下题号
            if (!result[i].equals(answers[i])) {
                scoresList.add(new Integer(i + 1).toString());
            }
        }
        String[] str = new String[scoresList.size()];
        scoresList.toArray(str);
        return str; // 返回一个得分集
    }

    // 获取完整的题目
    public String[] getQuestions() {
        String[] questions = new String[testNum];
        for (int i = 0; i < testNum; i++)
            questions[i] = question_1[i] + "   " + operator[i] + "   " + question_2[i] + "  =  ";
        return questions;
    }

    // 获取标准答案
    public String[] getStandardAnswer() {
        return result;
    }
        // 上传正确率
    public static void upDate() throws IOException{
//        try {
//            writer = new BufferedWriter(new FileWriter(new File("history/accuracy.txt")));
//            writer.write(right.toString());
//            writer.newLine();
//            writer.write(all.toString());
//            writer.newLine();
//            writer.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        File file = new File("history/scores.txt");
        if (file.exists()) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            while (true) {
                String readLine = reader.readLine();
                if (readLine == null) {
                    break;
                }
                scores.add(readLine);
            }
            reader.close();
        } else {
            file.createNewFile();
        }
    }
    /**
     * 保存成绩到文件
     * @throws FileNotFoundException 
     */
    public static void addscore(Integer score) throws FileNotFoundException {
        String form = date.format(new Date());
        PrintWriter pw = new PrintWriter(new File("history/scores.txt"));
        if (scores.size() >= 10) {//最多十次数据
            scores.remove(0);
        }
        scores.add(form + "-----" + score);
        for (String string : scores) {
            pw.println(string);
        }
        pw.flush();
        pw.close();
    }
    

(3)倒计时

public void Daojishi(int a) {

        boolean jishi = true;
        while (jishi == true) {

            a = --a;
            lblShow.setText("您还有   " + a + "   秒准备时间");
            if (a <= 10) {

                lblShow.setForeground(Color.red);
            }

            if (a == 0) {

                lblShow.setText("0");

                jishi = false;

                // JOptionPane.showMessageDialog(null, "时间到!");
                this.dispose();
                new Client();
            }
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();

            }
        }

    }

    // 创建显示时间线程

    class TimeThread extends Thread {

        public void run() {

            while (true) {

                String str = "";// 9:33:12

                Calendar c = Calendar.getInstance();

                int h = c.get(Calendar.HOUR_OF_DAY);

                int m = c.get(Calendar.MINUTE);

                int s = c.get(Calendar.SECOND);

                if (m < 10 && s >= 10) {

                    str = h + ":" + "0" + m + ":" + s;

                    lblShowTime.setText("现在时间:" + str);

                } else if (m < 10 && s < 10) {

                    str = h + ":" + "0" + m + ":" + "0" + s;

                    lblShowTime.setText("现在时间:" + str);

                } else if (m >= 10 && s < 10) {

                    str = h + ":" + m + ":" + "0" + s;

                    lblShowTime.setText("现在时间:" + str);

                } else if (m >= 10 && s >= 10) {

                    str = h + ":" + m + ":" + s;

                    lblShowTime.setText("现在时间:" + str);

                }

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

            }

        }

(4)统计成绩通过柱状图显示

public static CategoryDataset createDataset() // 创建柱状图数据集
    {

        DefaultCategoryDataset dataset = new DefaultCategoryDataset();

        int i = 0;
        for (String str : scores) {
            i++;
            String[] split = str.split("-----");
            dataset.addValue(Integer.parseInt(split[1]), "得分", "第" + i + "次");
        }

        return dataset;
    }

    public static JFreeChart createChart() // 用数据集创建一个图表
    {
        JFreeChart chart = ChartFactory.createBarChart("分数统计图", "分数统计", "得分",
                createDataset(), PlotOrientation.VERTICAL, false, true, false); // 创建一个JFreeChart
        
        chart.setBorderPaint(Color.blue);
        chart.setBackgroundPaint(Color.lightGray);
        chart.setTitle(new TextTitle("成绩分析图", FONT));// 可以重新设置标题,
        CategoryPlot plot = (CategoryPlot) chart.getPlot();// 获得图标中间部分,即plot
        plot.setDomainCrosshairPaint(Color.orange);
        CategoryAxis categoryAxis = plot.getDomainAxis();// 获得横坐标
        categoryAxis.setLabelFont(FONT);// 设置横坐标字体
        categoryAxis.setTickLabelFont(FONT); // x轴下标
        categoryAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);// 分类标签以45度倾斜
        ValueAxis rangeAxis = plot.getRangeAxis();// 获取柱状
        rangeAxis.setLabelFont(FONT); // Y轴标题
        
        // plot.setBackgroundPaint(Color.orange);//图片背景
        

        return chart;
    }

    public static JPanel createPanel() {
        JFreeChart chart = createChart();
        return new ChartPanel(chart); // 将chart对象放入Panel面板中去,ChartPanel类已继承Jpanel
    }

    public static void main(String[] args) {
        JFreeChartshow chart = new JFreeChartshow("成绩统计图");
        chart.pack();// 以合适的大小显示
        chart.setVisible(true);

    }
}      

6.运行结果

(1)输入用户名(admin),密码登录(123456)

(2)点击确定按钮,进入到答题准备页面,显示答题请做好准备开始答题。

(3)点击开始答题,选择题目类型。

 (4)右上角显示答题用时,不答或答错题目则视为零分。

                 

(5)点击成绩统计,可允许用户进行多轮测试,提供用户多轮测试分数柱状图 。

  

   TXT中显示如下:

    

7.结对编程效果是否真的1+1>2?

     结对编程效果确实是1+1>2的。通过此次和同伴一起编程,对于结对编程我有以下理解:        

     1.结对编程特别适合于知识的分享和传递。特别适合于帮助开发者快速熟悉自己所不熟悉的领域。对于新员工,效果特别明显。

     2. 如果两个人水平相似,并且对正在工作的领域都很了解的人,结对编程有浪费时间的嫌疑。但我对于这个前提是存疑的。除非特别简单的部分,很难有人真正能说对一个领域全面了解。

     3. 结对编程很累。要求结对的双方都要保证有充沛的精力。

     4. 结对的双方如果脾气对味,技术观点相近,结对会很愉快,效率有明显提高。反之,就可能陷入很多的争吵,而导致进度停滞不前。甚至影响团队协作。

     5. 不是所有的工作都适合结对。技术验证和架构设计都不适合结对。结对比较适合在需求和架构设计明确后的实现阶段。常见的方式有一个人写,一个人看;或者一个人写实现,一个人写测试。一段时间后交换。

    6. 结对编程不能替代代码评审。虽然结对编程对代码的审核程度比代码评审细致的多。但两个结对的人有明显的思维趋同性,从而忽略同样的问题或者犯下同样的错误。 

8.小结

      在本次课程设计中遇到的问题:

               这学期的结对编程对我们来说是一次全新的尝试,也是一次挑战。因为是初次用此模式,所以刚开始的时候,合作的不是太好,任务分配不均匀,反而拖慢了编程速度。

      解决的办法:

              通过反复交流,相互指正不足,逐渐改进。充分发挥结对编程的优点。

9.PSP

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

 

 

 

 

 

posted on 2018-04-03 23:46  Smile_Zhb  阅读(250)  评论(0编辑  收藏  举报