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 |