北京电子科技学院(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 :撤消上一步操作