• 实验三 敏捷开发与XP实践


    实验三 敏捷开发与XP实践

    实验内容

    1. XP基础

    2. XP核心实践

    3. 相关工具

    实验步骤

    (一)敏捷开发与XP

    软件工程包括下列领域:软件需求分析、软件设计、软件构建、软件测试和软件维护。

    标识符名字应当直观且可以拼读,可望文知意,不必进行“解码”,一般采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名,用词要准确例如“当前值”应该起名currentValue,写成nowValue就不准确了,但还凑合,写成dqz(dang qian zhi 首字母)就是笑话了。

    标识符的长度“min-length && max-information”的原则,比如:maxValmaxValueUntilOverflow要好些,可以通过去元音法把变量名变短,如returnValue->rtnValmessage->msg;一般全局变量用具有说明性的名字,局部变量用短名字:单字符的名字,常见的如i,j,k等用作局部变量。   

    (二)编码标准

    编写代码一个重要的认识是“程序大多时候是给人看的”,编程标准使代码更容易阅读和理解,甚至可以保证其中的错误更少。编程标准包含:具有说明性的名字、清晰的表达式、直截了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。

    标识符的长度“min-length && max-information”的原则,比如:maxValmaxValueUntilOverflow要好些,可以通过去元音法把变量名变短,如returnValue->rtnValmessage->msg;一般全局变量用具有说明性的名字,局部变量用短名字:单字符的名字,常见的如i,j,k等用作局部变量。

    Java中的一般的命名规则有:

    • 要体现各自的含义
    • 包、类、变量用名词
    • 方法名用动宾
    • 包名全部小写,如:io,awt
    • 类名第一个字母要大写,如:HelloWorldApp
    • 变量名第一个字母要小写,如:userName
    • 方法名第一个字母要小写:setName

    (三)结对编程

    武西垚工作:加注释,测试代码,加图片

    郑伟:写主函数,完善各模块

    结对同学:20135332

    http://www.cnblogs.com/wuxiyao/ 

    拼图游戏

    * JAVA小游戏-拼图 我做的第一个小游戏
    * Cell类是继承的按钮类,并加上相应图形,形成方格
    *MyCanvas是一个面板,加载Cell类的对象(方格),是这三个类中的核心
    *


    */

    import java.awt.BorderLayout;
    import java.awt.Button;
    import java.awt.Choice;
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.Icon;
    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;


    public class MyMainFrame extends JFrame implements ActionListener {
    MyCanvas myCanvas;
    JPanel panelNorth,panelPreview;//定义上方的面板,及预览所需的面板
    Button start,preview,set;//定义开始,预览,设定按钮
    Container container;//容器,得到内容面板

    public MyMainFrame() {//初使化
    container=this.getContentPane();
    start=new Button("开始");
    start.addActionListener(this);
    preview=new Button("预览");
    preview.addActionListener(this);
    set = new Button("设置");
    set.addActionListener(this);
    panelPreview=new JPanel();
    panelPreview.setLayout(null);
    Icon icon=new ImageIcon("pictrue/pic_"+MyCanvas.pictureID+".jpg");
    JLabel label=new JLabel(icon);
    label.setBounds(0,0,300,300);
    panelPreview.add(label);


    panelNorth=new JPanel();
    panelNorth.setBackground(Color.red);
    panelNorth.add(start);
    panelNorth.add(preview);
    panelNorth.add(set);
    myCanvas=new MyCanvas();
    container.add(myCanvas,BorderLayout.CENTER);
    container.add(panelNorth,BorderLayout.NORTH);
    this.setTitle("拼图小游戏-明");
    this.setLocation(300,200);
    this.setSize(308,365);
    this.setResizable(false);
    this.setVisible(true);

    this.setDefaultCloseOperation(3);
    }
    public static void main(String[] args) {
    // TODO 自动生成方法存根
    new MyMainFrame();

    }
    public void actionPerformed(ActionEvent arg0) {//对三个按钮事件的处理
    // TODO 自动生成方法存根
    Button button=(Button)arg0.getSource();
    if(button==start){
    myCanvas.Start();

    }else if(button==preview){
    if(button.getLabel()=="预览"){
    container.remove(myCanvas);
    container.add(panelPreview);
    panelPreview.updateUI();
    container.repaint();

    button.setLabel("返回");
    }else{
    container.remove(panelPreview);
    container.add(myCanvas);
    container.repaint();
    button.setLabel("预览");
    }
    }else if(button==set){//修改所选图片
    Choice pic = new Choice();
    pic.add("小猫");
    pic.add("小猪");
    pic.add("云");
    pic.add("QQ");
    pic.add("卡通");
    pic.add("花");

    int i=JOptionPane.showConfirmDialog(this, pic, "选择图片", JOptionPane.OK_CANCEL_OPTION);
    if(i==JOptionPane.YES_OPTION){
    MyCanvas.pictureID=pic.getSelectedIndex()+1;
    myCanvas.reLoadPictrue();
    Icon icon=new ImageIcon("pictrue/pic_"+MyCanvas.pictureID+".jpg");
    JLabel label=new JLabel(icon);
    label.setBounds(0,0,300,300);
    panelPreview.removeAll();
    panelPreview.add(label);
    panelPreview.repaint();
    }
    }
    }

    }

    --------------------------------------------------

    import java.awt.Rectangle;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;

    import javax.swing.Icon;
    import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;


    public class MyCanvas extends JPanel implements MouseListener {
    boolean hasAddActionListener=false;//设置方格的动作监听器的标志位,TRUE为已经添加上动作事件,FALSE是尚未添加动作事件
    Cell cell[];//定义方格
    Rectangle cellNull;//定义空方格区域
    public static int pictureID=1;//当前选择的图片代号
    public MyCanvas() {
    this.setLayout(null);
    this.setSize(400,400);
    cellNull=new Rectangle(200,200,100,100);//空方格区域在第三行每三列
    cell=new Cell[9];
    Icon icon;
    for (int i = 0; i < 3; i++) {//为9个方格加载图片,并初使化坐标,形成三行三列
    for(int j=0;j<3;j++){
    icon=new ImageIcon("pictrue/pic_"+pictureID+"_"+(i*3+j+1)+".jpg");
    cell[i*3+j]=new Cell(icon);
    cell[i*3+j].setLocation(j*100,i*100);
    this.add(cell[i*3+j]);
    }
    }
    this.remove(cell[8]);//移除最后一个多余的方格
    }

    public void reLoadPictrue(){//当选择其它图形进行拼图时,需重新加载新图片
    Icon icon;
    for (int i = 0; i < 3; i++) {
    for(int j=0;j<3;j++){
    icon=new ImageIcon("pictrue/pic_"+pictureID+"_"+(i*3+j+1)+".jpg");
    cell[i*3+j].setIcon(icon);
    }
    }
    }
    public boolean isFinish(){//判断是否拼合成功
    for(int i=0;i<8;i++){
    int x=cell[i].getBounds().x;
    int y=cell[i].getBounds().y;
    if(y/100*3+x/100!=i)
    return false;
    }
    return true;
    }

    public void Start(){//对方格进行重新排列,打乱顺序

    while(cell[0].getBounds().x<=100&&cell[0].getBounds().y<=100){//当第一个方格距左上角较近时
    int x=cellNull.getBounds().x;
    int y=cellNull.getBounds().y;
    int direction=(int)(Math.random()*4);//产生0-4,对应空方格的上下左右移动
    if(direction==0){//空方格左移动,与左侧方格互换位置,左侧方格右移动
    x-=100;
    if(test(x,y)){
    for(int j=0;j<8;j++){
    if((cell[j].getBounds().x==x)&&(cell[j].getBounds().y==y)){//依次寻找左侧的按钮
    cell[j].move("RIGHT",100);
    cellNull.setLocation(x,y);
    break;//找到后跳出for循环
    }
    }
    }
    }else if(direction==1){//RIGHT
    x+=100;
    if(test(x,y)){
    for(int j=0;j<8;j++){
    if((cell[j].getBounds().x==x)&&(cell[j].getBounds().y==y)){
    cell[j].move("LEFT",100);
    cellNull.setLocation(x,y);
    break;
    }
    }
    }
    }else if(direction==2){//UP
    y-=100;
    if(test(x,y)){
    for(int j=0;j<8;j++){
    if((cell[j].getBounds().x==x)&&(cell[j].getBounds().y==y)){
    cell[j].move("DOWN",100);
    cellNull.setLocation(x,y);
    break;
    }
    }
    }
    }else{//DOWN
    y+=100;
    if(test(x,y)){
    for(int j=0;j<8;j++){
    if((cell[j].getBounds().x==x)&&(cell[j].getBounds().y==y)){
    cell[j].move("UP",100);
    cellNull.setLocation(x,y);
    break;
    }
    }
    }
    }

    }

    if(!hasAddActionListener)//如果尚未添加动作事件,则添加
    for(int i=0;i<8;i++)//为第个方格添加动作事件,这样单击按钮就能移动了
    cell[i].addMouseListener(this);
    hasAddActionListener=true;
    }
    private boolean test(int x,int y){
    if((x>=0&&x<=200)||(y>=0&&y<=200))
    return true;
    else
    return false;
    }
    // public void paint(Graphics g){
    //
    // for(int i=0;i<=300;i+=100)
    // g.drawLine(0, i, 300, i);
    // for(int i=0;i<=300;i+=100)
    // g.drawLine(i, 0, i, 300);
    // for(int i=0;i<8;i++)
    // cell[i].repaint();
    // }

    public void mouseClicked(MouseEvent arg0) { }
    public void mouseEntered(MouseEvent arg0) { }
    public void mouseExited(MouseEvent arg0) { }
    public void mouseReleased(MouseEvent arg0) { }
    public void mousePressed(MouseEvent arg0) {//方格的鼠标事件,因为用到了MyCanvas中的一些方法,因此没有在Cell类中处理鼠标事件
    Cell button=(Cell)arg0.getSource();
    int x1=button.getBounds().x;//得到所单击方格的坐标
    int y1=button.getBounds().y;

    int x2=cellNull.getBounds().x;//得到空方格的坐标
    int y2=cellNull.getBounds().y;

    if(x1==x2&&y1-y2==100)//进行比较,如果满足条件则进行交换
    button.move("UP",100);
    else if(x1==x2&&y1-y2==-100)
    button.move("DOWN",100);
    else if(x1-x2==100&y1==y2)
    button.move("LEFT",100);
    else if(x1-x2==-100&&y1==y2)
    button.move("RIGHT",100);
    else
    return;//不满足就不进行任何处理

    cellNull.setLocation(x1,y1);
    this.repaint();
    if(this.isFinish()){//进行是否完成的判断
    JOptionPane.showMessageDialog(this,"恭喜你完成拼图,加油!");
    for(int i=0;i<8;i++)
    cell[i].removeMouseListener(this);//如果已完成,撤消鼠标事件,鼠标单击方格不在起作用
    hasAddActionListener=false;
    }
    }


    }

    ------------------------------------------------------------------------------------------

    import javax.swing.Icon;
    import javax.swing.JButton;

    public class Cell extends JButton {
    Cell(Icon icon){//实际为ICON
    super(icon);
    this.setSize(100,100);
    }

    public void move(String direction,int sleep){//方格的移动
    if(direction=="UP"){
    this.setLocation(this.getBounds().x,this.getBounds().y-100);
    }else if(direction=="DOWN"){
    this.setLocation(this.getBounds().x,this.getBounds().y+100);
    }else if(direction=="LEFT"){
    this.setLocation(this.getBounds().x-100,this.getBounds().y);
    }else{
    this.setLocation(this.getBounds().x+100,this.getBounds().y);
    }
    }

    }

     

    (四)版本控制

    注意一点,往代码库提交的代码一定编译、运行、测试都没有问题的代码,我们上面测试代码没有问题了,就可以提交了: 如图:我们可以先用git status查看一下代码状态,显示有未跟踪的代码,并建议用git add <file>...添加,我们使用git add HelloWorld.* 把要提交的文件的信息添加到索引库中。当我们使用git commit时,git将依据索引库中的内容来进行文件的提交。这只是在本地操作,关闭实验环境,会删除代码的,如果想把代码保存到远程托管服务器中,需要使用git push实验完成前,一定不要忘了使用git push,否则就是相当于你在Word中编辑了半天文件最后却没有保存。 我们可以修改HelloWorld.java,如下图所示:

    编译、运行、测试没有问题后进行提交,这儿使用的是git commit -a: ,

    我们可以通过git log查看代码提交记录:

    (五)重构

    我们先看看重构的概念:

    重构(Refactor),就是在不改变软件外部行为的基础上,改变软件内部的结构,使其更加易于阅读、易于维护和易于变更 。

    重构中一个非常关键的前提就是“不改变软件外部行为”,它保证了我们在重构原有系统的同时,不会为原系统带来新的BUG,以确保重构的安全。如何保证不改变软件外部行为?重构后的代码要能通过单元测试。如何使其更加易于阅读、易于维护和易于变更 设计模式给出了重构的目标。

    重构重要吗?你看看Eclipse菜单中有个refactor菜单就知道了,重构几乎是现代IDE的标配了:

    我们在编码标准中说“给标识符命名”是程序员一项重要技能,以前没有这个意识,现在知道了怎么办?没问题,上图中重构的第一项功能就是Rename,可以给类、包、方法、变量改名字。 例如这有个ABC类:

    这个类,类名,方法名和方法的参数名都有问题,没有注释的话是无法理解代码的。我们可以使用Eclipse中的重构功能来改名。修改方法是,用鼠标单击要改的名字,选择Eclipse中菜单中的Refactor->Rename...

    重构完的效果如下:

    功能不变,代码水平立马上了一个档次,体会到命名的威力了吧?

    Eclipse中菜单中的Refactor->Encapsulate Field...,如下图:

    注意分析一下重构前后的代码变化: 同样可以封装idage两个成员变量,结果如下:

    上面第34,35行还是有问题的,每次打印学生信息都这么写代码违反了DRY原则,造成代码重复,正常的重构可以使用Eclipse中的Extract Method...,如下图:

    由于Java中所有的类都有个专门的toString方法,我们使用Eclipse中Source->Generate toString()...Student类产生一个toString方法,如下图:

    修改main的代码,结果如下:

    大家想一想,这样重构后有什么好处?重构有什么问题吗?

    我们要修改软件,万变不离其宗,无非就是四种动机:

    • 增加新功能;
    • 原有功能有BUG;
    • 改善原有程序的结构;
    • 优化原有系统的性能 。

     

    • 最单纯的Duplicated Code就是[同一个class内的两个方法含有相同表达式(expression)]。这时候你需要做的就是采用Extract Method提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。
    • 另一种常见情况就是[两个互为兄弟(sibling)的subclasses内含有相同表达式]。要避免这种情况,只需要对两个classes都使用Extract Method,然后再对被提炼出的代码使用Pull Up Method,将它推入superclass内。
    • 如果代码之间只是类似,并非完全相同,那么就得运用Extract Method将相似部分和差异部分割开,构成单独一个方法。然后你可能发现或许可以运用Form Template Method获得一个Template Method设计模式。
    • 如果有些方法以不同的算法做相同的事,你可以择定其中较清晰的一个,并使用Substitute Algorithm将其它方法的算法替换掉。
    • 如果两个毫不相关的classes内出现Duplicaded Code,你应该考虑对其中一个使用Extract Class,将重复代码提炼到一个独立class中,然后在另一个class内使用这个新class。但是,重复代码所在的方法也可能的确只应该属于某个class,另一个class只能调用它,抑或这个方法可能属于第三个class,而另两个classes应该引用这第三个class。你必须决定这个方法放在哪儿最合适,并确保它被安置后就不会再在其它任何地方出现。

    其他Bad Smell与相应的重构手法如下表所示:

    Eclipse中Refactor菜单中的重构手法的应用时机如下图所示:

    更完整的手法可以参考《重构》作者Martin Fowler的博客。Eclipse中基本手法的使用大家可以参考任何人都可以重构来进行学习实践。

    一个完整的重构流程包括:

    1. 从版本控制系统代码库中Check out code
    2. 读懂代码(包括测试代码)
    3. 发现bad smell
    4. Refactoring
    5. 运行所有的Unit Tests
    6. 往代码库中Check in code

    我们结合Git给出一个比较完整的例子。

    (六)实践项目

    1. 以结对编程的方式编写一个软件,Blog中要给出结对同学的Blog网址,可以拍照展现结对编程情况,可以参考一下其他学校的作业

         

    结对同学:20135332

    http://www.cnblogs.com/wuxiyao/ 

    2.记录TDD和重构的过程,测试代码不要少于业务代码,Eclipse中refactor菜单下的重构技能不要少于5个

    3.团队代码要使用git在实验楼中托管,要使用结对同学中的一个同学的账号托管。

    4. 程序要有GUI界面,参考用户界面和用户体验

    5.程序功能从豌豆荚游戏中选择一款用Java实现,注意:团队之间项目不能有重复,课代表协调一下

    6.实验报告中统计自己的PSP(Personal Software Process)时间

    步骤耗时百分比
    需求分析 40min   
    设计 50min   
    代码实现 324min   
    测试 300min   
    分析总结 400min 

    参考资料

    1.《解析极限编程》 2.《构建之法电子版)》,著者邹欣Blog 3.《结对编程技术》 4.《版本控制之道》 5.《重构》 6.《重构与模式》 7.《程序设计实践

  • 相关阅读:
    课程个人总结
    构建之法阅读笔记06
    构建之法读后感5
    第五周进度条
    提高自身能力
    活动图与状态机图
    对分析业务模型----类图的学习与认识
    需求分析工作的基本道理
    问题账户需求分析
    2016秋季个人阅读计划
  • 原文地址:https://www.cnblogs.com/zhengwei0712/p/4552232.html
Copyright © 2020-2023  润新知