实验三 敏捷开发与XP实践
实验内容:
1.XP基础
2.XP核心实践
3.相关工具
实验步骤
(一)敏捷开发与XP
软件工程=开发流程+工具
软件=程序+软件工程
软件企业=软件+商业模式
敏捷开发是一种以人为核心,迭代、循序渐进的开发方法。
开发宣言:
A.个体交互 胜过过程和工具 (人是获得成功的最为重要的因素)
B.可以工作的软件 胜过面面俱到的文档(文档需适度)
C.客户合作 胜过合同谈判 (双赢比输赢更好)
D.响应变化 胜过遵循计划 (为以后做详略得当的计划)
极限编程(XP)
XP准则
一项实践在XP环境中成功使用的依据通过XP的法则呈现,包括:快速反馈、假设简单性、递增更改、提倡更改、优质工作。
XP软件开发的基石是XP的活动,包括:编码、测试、倾听、设计。
项目成员用户成功执行XP活动的技术通过XP实践来呈现,包括编程、团队、过程相关的以测试为核心的开发流程:
(二)编码标准
编程标准包含:具有说明性的名字、清晰的表达式、直截了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。
单击Eclipse菜单中的source->Format 或用快捷键Ctrl+Shift+F就可以按Eclipse规定的规范缩进
举例:
Java中一般的命名规则
标识符名字应当直观且可以拼读,可望文知意,不必进行“解码”,一般采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名,用词要准确例如“当前值”应该起名currentValue,写成nowValue就不准确了,但还凑合,写成dqz(dang qian zhi 首字母)就是笑话了。
标识符的长度“min-length && max-information”的原则,比如:maxVal 比maxValueUntilOverflow要好些,可以通过去元音法把变量名变短,如returnValue->rtnVal ,message->msg;一般全局变量用具有说明性的名字,局部变量用短名字:单字符的名字,常见的如i,j,k等用作局部变量。
编写代码的七个原则:
(三)结对编程
驾驶员(Driver)是控制键盘输入的人。
领航员(Navigator)起到领航、提醒的作用。
驾驶员:写设计文档,进行编码和单元测试等XP开发流程。
领航员:审阅驾驶员的文档、驾驶员对编码等开发流程的执行;考虑单元测试的覆盖率;思考是否需要和如何重构;帮助驾驶员解决具体的技术问题。
驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。没有“我的代码”、“你的代码”或“他/她的代码”,只有“我们的代码”。
只有水平上的差距,没有级别上的差异。两人结对,尽管可能大家的级别资历不同,但不管在分析、设计或编码上,双方都拥有平等的决策权利。
(四)版本控制
版本控制的好处:
版本控制提供项目级的 undo(撤销) 功能: 没有什么事情是终结版本, 任何错误必须很容易回滚。 假设你在使用世界上最复杂的文字处理系统。 它具备了所有的能想到的功能,就是没有支持 DELETE(删除) 键。想象你打字的时候得多么的谨慎和缓慢吧, 特别是一篇超大的文档的快临近末尾的时候, 一个不小心就要重头再来(试想你选中所有的文字, 不小心按了 DELETE 键, 因为没有撤销功能,只好重新录入)。编辑文字和版本控制相同,任何时候都需要回滚,无论是一个小时, 一天, 还是一周, 这让你的团队工作自由快速的工作, 而且对于修正错误也非常自信。
版本控制允许多人在同一代码上工作, 只要遵守一定的控制原则就行。 再也不会发生诸如一个人覆盖了另一个人编辑的代码,导致那个人的修改无效这样的情况。
版本控制系统保存了过去所作的修改的历史记录。如果你遭遇到一些惊讶的代码,通过版本控制系统可以很容易找出是谁干的, 修改了什么, 修改的时间, 如果幸运的话,还能找出原因。
版本控制系统还支持在主线上开发的同时发布多个软件版本。在软件发布的时候也不需要整个团队的停止工作,不需要冻结代码。
版本控制也是项目级的时间机器,你可以选择任何一个时间, 精确地查看项目在当时的情况。 这对研究非常有用, 也是重现以前某个有问题的发布版本的基础。
流行的版本控制工具有CVS,SVN,Git等,更多的可以参考这里。Git是Linus除了Linux操作系统外的另外一个重要发明。
首先开通我的代码库功能。
我们给一个HelloWorld
的例子: 首先进入Code
目录,你会发现有了shiyanlou_cs212
目录,进入shiyanlou_cs212
,创建HelloWorld
目录,创建并编辑HelloWorld.java
文件
如图:我们可以先用git status
查看一下代码状态,显示有未跟踪的代码,并建议用git add <file>...
添加,我们使用git add HelloWorld.*
把要提交的文件的信息添加到索引库中。当我们使用git commit
时,git将依据索引库中的内容来进行文件的提交。这只是在本地操作,关闭实验环境,会删除代码的,如果想把代码保存到远程托管服务器中,需要使用git push
,实验完成前,一定不要忘了使用git push
,否则就是相当于你在Word中编辑了半天文件最后却没有保存。 我们可以修改HelloWorld.java
,如下图所示:编译、运行、测试没有问题后进行提交,这儿使用的是git commit -a
:
(五)重构
重构(Refactor),就是在不改变软件外部行为的基础上,改变软件内部的结构,使其更加易于阅读、易于维护和易于变更 。重构中一个非常关键的前提就是“不改变软件外部行为”,它保证了我们在重构原有系统的同时,不会为原系统带来新的BUG,以确保重构的安全。
Refactor——Rename:给类、包、方法、变量改名字
Refactor——Encapsulate Field
学过C语言的学生学Java时常犯的毛病是不会封装,该用类的地方都用了结构体。比如要定义一个类Student
,会出现这样的代码:
同样可以封装id
和age
两个成员变量,结果如下:
由于Java中所有的类都有个专门的toString方法,我们使用Eclipse中Source->Generate toString()... 给Student类产生一个toString方法
最单纯的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。你必须决定这个方法放在哪儿最合适,并确保它被安置后就不会再在其它任何地方出现。
*一个完整的重构流程包括:
从版本控制系统代码库中Check out code
读懂代码(包括测试代码)
发现bad smell
Refactoring
运行所有的Unit Tests
往代码库中Check in code
(六)实践项目
1. 以结对编程的方式编写一个软件,Blog中要给出结对同学的Blog网址,可以拍照展现结对编程情况,可以参考一下其他学校的作业
队友blog网址:http://www.cnblogs.com/pianogirl/p/4553397.html
2.代码
基本计算器
3.GUI界面
//面板容器类file name:CalculatorPanelClass.java
package Game.Calculator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CalculatorPanelClass extends JPanel
{
private JLabel display;
private JPanel panel;
private double result;
private String lastCommand;
private boolean start;
public CalculatorPanelClass()
{
setLayout(new BorderLayout());
result = 0;
lastCommand = "=";
start = true;;
display = new JLabel("0",SwingConstants.RIGHT);
display.setForeground(Color.black); //设置前景颜色
display.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.black),
BorderFactory.createEmptyBorder(5,5,5,5)));
add(display, BorderLayout.NORTH);
ActionListener insert = new InsertAction();
ActionListener command = new CommandAction();
panel = new JPanel();
panel.setLayout(new GridLayout(4,4));//计算器按钮
addButton("7",insert);
addButton("8",insert);
addButton("9",insert);
addButton("/",command);
addButton("4",insert);
addButton("5",insert);
addButton("6",insert);
addButton("*",command);
addButton("1",insert);
addButton("2",insert);
addButton("3",insert);
addButton("-",command);
addButton("0",insert);
addButton(".",insert);
addButton("=",command);
addButton("+",command);
add(panel,BorderLayout.CENTER);
}
//在面板容器中添加按钮组件
private void addButton(String label,ActionListener listener)
{
JButton button = new JButton(label);
button.addActionListener(listener);
panel.add(button);
}
//处理单击数字按钮事件的监听器类
private class InsertAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
if (start)
{
display.setText("");
start = false;
}
display.setText(display.getText() + input);
}
}
//处理单击命令按钮事件的监听器类
private class CommandAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
String command = evt.getActionCommand();
if(start)
{
lastCommand = command;
}
else
{
calculate(Double.parseDouble(display.getText()));
lastCommand = command;
start = true;
}
}
}
public void calculate(double x)
{
if (lastCommand.equals("+"))
{
result += x;
}else if (lastCommand.equals("-"))
{
result -= x;
}else if (lastCommand.equals("*"))
{
result *= x;
}else if (lastCommand.equals("/"))
{
result /= x;
}else if (lastCommand.equals("="))
{
result = x;
}
display.setText("" +result);
}
}
//定义外层窗口类CalculatorFrameClass
//file name:CalculatorFrameClass.java
package Game.Calculator;
import java.awt.*;
import javax.swing.*;
public class CalculatorFrameClass extends JFrame//外层窗口类
{
public CalculatorFrameClass()
{
setTitle("Calculator");
Container contentPane = getContentPane();
CalculatorPanelClass panel = new CalculatorPanelClass();
contentPane.add(panel);
setSize(400,400);
setVisible(true);
setResizable(false);
}
}
//测试类TestCalculatorClass.java
package Game.Calculator;
import javax.swing.*;
public class TestCalculatorClass
{
public static void main(String[] args)
{
CalculatorFrameClass frame = new CalculatorFrameClass();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
心得体会
本次实验使我学到很多,不仅仅是关于Java编程中版本控制及重构的相关知识,还有很多来源于小组结对的配合。两人的合作分工,相互协作,相互磨合、碰撞,到达成共识。
统计的PSP(Personal Software Process)时间
步骤 |
耗时(min) |
百分比 |
需求分析 |
20~30 |
10% |
设计 |
40~50 |
15% |
代码实现 |
90~100 |
50% |
测试 |
40~50 |
20% |
分析总结 |
10~15 |
5%
|