北京电子科技学院(BESTI)
实 验 报 告
课程:Java程序设计 班级:1353 姓名:韩玉琪 学号:20135317
成绩: 指导教师:娄嘉鹏 实验日期:2015.5.29
实验密级: 预习程度: 实验时间:15:30--18:00
仪器组次: 必修/选修: 选修 实验序号:3
实验名称:实验三 敏捷开发与XP实践
实验目的与要求:
1. XP基础
2. XP核心实践
3. 相关工具
实验仪器:
名称 |
型号 |
数量 |
PC |
Lenovo |
1 |
实验楼环境 |
/ |
1 |
Eclipse |
/ |
1 |
一、实验内容及步骤
(一)敏捷开发与XP
1.了解什么是敏捷开发
敏捷开发(Agile Development)是一种以人为核心、迭代、循序渐进的开发方法。“敏捷流程”是一系列价值观和方法论的集合。
2.认识XP(极限编程)
极限编程是一种全新而快捷的软件开发方法。XP团队使用现场客户、特殊计划方法和持续测试来提供快速的反馈和全面的交流:
(1)XP是以开发符合客户需要的软件为目标而产生的一种方法论
(2)XP是一种以实践为基础的软件工程过程和思想
(3)XP认为代码质量的重要程度超出人们一般所认为的程度
(4)XP特别适合于小型的有责任心的、自觉自励的团队开发需求不确定或者迅速变化的软件
3.XP软件开发的准则:沟通、简单、反馈、勇气
XP软件开发的基石:编码、测试、倾听、设计
4.我们关注的XP实践:编码标准,结对编程,代码集体所有,测试,重构等。通过学习这些实践,可以形成以测试为核心的开发流程:
(二)编码标准
1.编程标准是具有说明性的名字、清晰的表达式、直截了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。
2.例:
在Eclipse中解决:单击Eclipse菜单中的source->Format 或用快捷键Ctrl+Shift+F就可以按Eclipse规定的规范缩进,效果如下:
3. Java中的一般的命名规则
(1)要使命名有一定意义
(2)包、类、变量用名词
(3)方法名用动宾
(4)包名全部小写,如:io,awt
(5)类名第一个字母要大写,如:HelloWorldApp
(6)变量名第一个字母要小写,如:userName
(7)方法名第一个字母要小写:setName
(三)结对编程
1.结对编程的角色
(1)驾驶员(Driver)是控制键盘输入的人。
(2)领航员(Navigator)起到领航、提醒的作用。
2.如何更好地实现结对编程
(1)驾驶员:写设计文档,进行编码和单元测试等XP开发流程。
(2)领航员:审阅驾驶员的文档、驾驶员对编码等开发流程的执行;考虑单元测试的覆盖率;思考是否需要和如何重构;帮助驾驶员解决具体的技术问题。
(3)驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
(4)主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。
(5)只有水平上的差距,没有级别上的差异。
3.团队合作:同理心、互利、对事情不对人。
(四)版本控制
1.优点
(1)版本控制提供项目级的 undo(撤销)功能 没有什么事情是终结版本,任何错误必须很容易回滚。
(2)版本控制允许多人在同一代码上工作,只要遵守一定的控制原则就行。
(3)版本控制系统保存了过去所作的修改的历史记录。
(4)版本控制系统支持在主线上开发的同时发布多个软件版本。
(5)版本控制也是项目级的时间机器,你可以选择任何一个时间, 精确地查看项目在当时的情况。
2.开通我的代码库:http://git.shiyanlou.com/hyq20135317
3.学习如何将代码提交到代码库
$ cd /home/shiyanlou/Code/shiyanlou_cs212
# 修改代码文件
# 添加修改文件
$ git add 所有修改的文件
# 提交到环境中本地代码仓库
$ git commit -m '本次修改的描述'
# push到git.shiyanlou.com,无需输入密码
$ git push
4.实际操作:
进入目录→创建vi文本→编译成功→运行成功(见下图)→将代码push到代码库中(我的失误,这个截图丢了….但是后面有push自己编写的小游戏的代码)
Git log HelloWorld查看记录:
(五)重构
1. 概念
重构(Refactor),就是在不改变软件外部行为的基础上,改变软件内部的结构,使其更加易于阅读、易于维护和易于变更。
2. eclipse中Refactor
重新命名:Rename
封装成员变量:Encapsulate Field...
抽象方法:Extract Method...
3.实用的方法
4. 完整的重构流程包括:
从版本控制系统代码库中Check out code→读懂代码(包括测试代码)→发现bad smell→Refactoring→运行所有的Unit Tests→往代码库中Check in code
(六)实践项目
1.团队成员
结对伙伴:20135337 朱荟潼
博客:http://www.cnblogs.com/zzzz5
主要负责:伪代码编写、测试代码编写
20135317韩玉琪
Git:http://git.shiyanlou.com/hyq20135317
主要负责:产品代码编写、用户测试并程序修改完善代码
2.伪代码、测试代码(见http://www.cnblogs.com/zzzz5)
3.产品代码
包含三个大部分:
(1)入口:
package game;
public class AnswerQuestion {
public static void main(String args[]) {
new Function();
}
}
(2)设置GUI界面及其功能:
public class Function implements ActionListener{
public Function(){
//初始化面板,调用响应功能
}
public void readFile() {
//以特殊字符为标识,读取文件中相应内容
}
public void ifRight() {
//判断答题者选择的对错
}
public void setQuestion(int index) {
//将下标为index的题目内容显示出来
}
public void actionPerformed(ActionEvent e) {
//监听用户操作,并执行对应操作
}
}
详细设计:
package game;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
public class Function implements ActionListener {
private static final String SRC_GAMEFILE_题目_TXT = "src/gamefile/题目.txt";
private static final int Q_Number = 100;
JFrame f = new JFrame("答题练习小游戏");
JPanel p1 = new JPanel();
JPanel p2 = new JPanel();
JPanel p3 = new JPanel();
JPanel p4 = new JPanel();
int index = 0, total = 0;
QuestionBank questions[] = new QuestionBank[Q_Number];
JLabel questionString = new JLabel("题目:");
JLabel choiceString = new JLabel("选项:");
JLabel totalString = new JLabel("你的得分:");
TextArea question = new TextArea("\t游戏规则:\n\t1.点击“开始”进行答题,点击下一题继续\n\t2.答题过程中点击“重新练习”,从第一道题作答,分数归0。\n\t3.答题过程中点击“开始”从第一道题作答,分数累计.\n\n\t\t现在,请单击<开始>进行游戏", 5, 60, 3);
JTextField totaltf = new JTextField("0", 5);
JRadioButton choiceA = new JRadioButton("a");
JRadioButton choiceB = new JRadioButton("b");
JRadioButton choiceC = new JRadioButton("c");
JRadioButton choiceD = new JRadioButton("d");
ButtonGroup Group = new ButtonGroup();
JButton beginbt = new JButton("开始");
JButton nextbt = new JButton("下一个题目");
JButton practiceAgainbt = new JButton("重新练习");
File file = new File(SRC_GAMEFILE_题目_TXT);
public Function() {
f.setSize(750, 600);
f.setLocation(250, 50);
f.setVisible(true);
f.setLayout(new GridLayout(4, 1, 15, 15));
p1.setLayout(new FlowLayout(FlowLayout.CENTER));
p2.setLayout(new FlowLayout(FlowLayout.CENTER));
p3.setLayout(new FlowLayout(FlowLayout.CENTER));
p4.setLayout(new FlowLayout(FlowLayout.CENTER));
Group.add(choiceA);
Group.add(choiceB);
Group.add(choiceC);
Group.add(choiceD);
p1.add(questionString);
p1.add(question);
p2.add(choiceString);
p2.add(choiceA);
p2.add(choiceB);
p2.add(choiceC);
p2.add(choiceD);
p3.add(totalString);
p3.add(totaltf);
p4.add(beginbt);
p4.add(nextbt);
p4.add(practiceAgainbt);
f.add(p1);
f.add(p2);
f.add(p3);
f.add(p4);
nextbt.addActionListener(this);
beginbt.addActionListener(this);
practiceAgainbt.addActionListener(this);
readFile();
}
public void readFile() {
String s, question, choiceA, choiceB, choiceC, choiceD, answer;
try {
int count = 0;
FileInputStream in = new FileInputStream(file);
byte b[] = new byte[in.available()];
in.read(b);
s = new String(b);
String c[] = s.split("&&");
for (int i = 0; i < c.length; i++) {
question = c[i++];
choiceA = c[i++];
choiceB = c[i++];
choiceC = c[i++];
choiceD = c[i++];
answer = c[i];
questions[count++] = new QuestionBank(question, choiceA,choiceB, choiceC, choiceD, answer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void ifRight() {
if (choiceA.isSelected()) {
if (questions[index].answer.equals(choiceA.getText())) {
total++;
}
}
if (choiceB.isSelected()) {
if (questions[index].answer.equals(choiceB.getText())) {
total++;
}
}
if (choiceC.isSelected()) {
if (questions[index].answer.equals(choiceC.getText())) {
total++;
}
}
if (choiceD.isSelected()) {
if (questions[index].answer.equals(choiceD.getText())) {
total++;
}
}
totaltf.setText(Integer.toString(total));
}
public void setQuestion(int index) {
question.setText(questions[index].question);
choiceA.setText(questions[index].choiceA);
choiceB.setText(questions[index].choiceB);
choiceC.setText(questions[index].choiceC);
choiceD.setText(questions[index].choiceD);
Group.clearSelection();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == nextbt) {
ifRight();
if (index < questions.length) {
setQuestion(++index);
}
}
if (e.getSource() == practiceAgainbt) {
totaltf.setText(Integer.toString(0));
index = 0;
total = 0;
tryAgain();
}
if (e.getSource() == beginbt) {
index=0;
if (total != 0) {
tryAgain();
}else{
setQuestion(0);
}
}
}
private void tryAgain() {
ifRight();
if (index < questions.length) {
setQuestion(index);
}
}}
(3)存放文件中读取出的题目:
package game;
public class QuestionBank {
String question;
String choiceA, choiceB, choiceC, choiceD;
String answer;
QuestionBank(String question, String choiceA, String choiceB,String choiceC, String choiceD, String answer) {
this.question = question;
this.choiceA = choiceA;
this.choiceB = choiceB;
this.choiceC = choiceC;
this.choiceD = choiceD;
this.answer = answer;
}
}
4.遇到的问题与改进
(1)最开始写好了之后,在界面上测试,发现点击“重新练习”之后题目马上变成了第一道,但是分数还是原来的分数,而且在第一道题打完之后,不论答得对错,得分都会变成零分。
当时写的代码是这样的:
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == nextbt)
{
ifRight();
if(index<questions.length)
{
setQuestion(++index);
}
}
if(e.getSource() == beginbt)
{
setQuestion(0);
}
if(e.getSource() == practiceAgainbt)
{
setQuestion(0);
total = 0;
}
}
我认为在ifRight中已经有totaltf.setText(Integer.toString(total));就不需要再这里加了,但是显然不行。于是就将total=0前面加了一句:
totaltf.setText(Integer.toString(0));
但是问题又来了,虽然点击“重新开始”之后得分已经变成了0,但是第一道题不论对错,依然是0分。
这个问题困扰了我很久。在多次尝试修改无果之后。我自己开始反思这个思路是不是正确,然后发现,可能我在调用ifRight的时候就是这个监控的另一次了?所以我打算增加代码判断,就改成了:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == nextbt) {
ifRight();
if (index < questions.length) {
setQuestion(++index);
}
}
if (e.getSource() == practiceAgainbt) {
totaltf.setText(Integer.toString(0));
index = 0;
total = 0;
ifRight();
if (index < questions.length) {
setQuestion(index);
}
}
if (e.getSource() == beginbt) {
index=0;
if (total != 0) {
ifRight();
if (index < questions.length) {
setQuestion(index);
}
}else{
setQuestion(0);
}
}
}
将其中ifRight();
if (index < questions.length) {setQuestion(index);}部分
方法用重构抽取,命名为tryAgain
(2)文件相对路径无法打开
开始的时候文件我用的是绝对路径,但是后来再放进src中的时候,文件一直无法打开。然后我尝试了很多次,发现可能是eclipse自身的问题读不出来,然后就在eclipse里面建立一个txt文件,成功打开。
(3)重构:(掌握的不够好)
重新命名
抽取常量
private static final String SRC_GAMEFILE_题目_TXT = "src/gamefile/题目.txt";
private static final int Q_Number = 100;
抽取方法
private void tryAgain() {
ifRight();
if (index < questions.length) {
setQuestion(index);
}
}
5.运行
6.将代码push到代码库(因为实验楼不认中文,所以做了一些改动)
二、实验总结
步骤 |
耗时 |
百分比 |
需求分析 |
25min |
4% |
设计 |
30min |
5% |
代码实现 |
360min |
57% |
测试 |
150min |
23% |
分析总结 |
65min |
10% |
1.每次java实验都是一个大工程,这次尤其难。因为之前学的不扎实,应用能力弱,所以花的时间特别多,每天一下课就开始弄,晚上还熬夜;做出来的东西还特别low;遇到问题特别烦,就是上面的那个得分的问题,一直整了两天才改成符合最初设计构想的代码。
2.我觉得实验楼是一个很好的平台,老师每次的实验内容都很丰富,但是总感觉有点吃不消。就像这次的重构,虽然能跟着做,简单的也会,但是真正在自己的程序里用起来好难啊。还是没有修炼到家,需要提高自己的能力。
3.结对编程的方式、版本控制的方法都掌握的不够熟练,还需要继续学习。
4.这次我觉得我意外的一个收获是把VIM编辑器的基本操作学会了
按一下i键,下端显示 --INSERT--插入命令
Esc:退出i(插入)命令进行其它命令使用(实在不行Ctrl+C也好使)
wq:保存文件并退出
ZZ:保存文件并退出,同上一个命令,注意大写
q!:强制退出,不保存
u :撤消上一步操作