java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料

保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。

北京电子科技学院

课程:移动平台应用开发实践 班级:201592 姓名:苏泽楠 学号:20159207

成绩: 指导教师:娄嘉鹏 实验日期:2015.10.15

实验密级: 预习程度: 实验时间:

仪器组次: 必修/选修:选修 实验序号:3

实验名称: 敏捷开发与XP实践

实验内容

1. XP基础

2. XP核心实践

3. 相关工具

实验步骤

(一)敏捷开发与XP

软件工程是把系统的、有序的、可量化的方法应用到软件的开发、运营和维护上的过程。软件工程包括下列领域:软件需求分析、软件设计、软件构建、软件测试和软件维护。 人们在开发、运营、维护软件的过程中有很多技术、做法、习惯和思想体系。软件工程把这些相关的技术和过程统一到一个体系中,叫“软件开发流程”。软件开发流程的目的是为了提高软件开发、运营、维护的效率,并提高软件的质量、用户满意度、可靠性和软件的可维护性。 光有各种流程的思想是不够的,我们还要有一系列的工具来保证这些思想能够在实践中有效率地运作。软件开发很重要的一点不是看你能对多少理论讲的头头是道,还要看你对相关工具应用的如何,比如Java中单元测试要和JUnit的应用结合起来,建模要和Umbrello或StarUML的应用结合起来。编程学习是一个习而学的过程。 一个常见的公式是:软件工程=开发流程+工具 邹欣老师给出的两个公式:软件=程序+软件工程和软件企业=软件+商业模式 开发流程大家可以参考学习邹欣老师的软件团队和开发流程。常见的开发流程有:

RUP(Rational Unified Process)

PSP(Personal Software Process )

TSP(Team Software Process )

Agile Process

……

敏捷开发(Agile Development)是一种以人为核心、迭代、循序渐进的开发方法。“敏捷流程”是一系列价值观和方法论的集合。从2001年开始,一些软件界的专家开始倡导“敏捷”的价值观和流程,他们肯定了流行做法的价值,但是强调敏捷的做法更能带来价值。

其中,极限编程(eXtreme Programming,XP)是

是一种全新而快捷的软件开发方法。XP团队使用现场客户、特殊计划方法和持续测试来提供快速的反馈和全面的交流:

XP是以开发符合客户需要的软件为目标而产生的一种方法论

XP是一种以实践为基础的软件工程过程和思想

XP认为代码质量的重要程度超出人们一般所认为的程度

XP特别适合于小型的有责任心的、自觉自励的团队开发需求不确定或者迅速变化的软件

XP软件开发是什么样的通过 XP准则来表达:

沟通 :XP认为项目成员之间的沟通是项目成功的关键,并把沟通看作项目中间协调与合作的主要推动因素。

简单 :XP假定未来不能可靠地预测,在现在考虑它从经济上是不明智的,所以不应该过多考虑未来的问题而是应该集中力量解决燃眉之急。

反馈 :XP认为系统本身及其代码是报告系统开发进度和状态的可靠依据。系统开发状态的反馈可以作为一种确定系统开发进度和决定系统下一步开发方向的手段。

勇气:代表了XP认为人是软件开发中最重要的一个方面的观点。在一个软件产品的开发中人的参与贯穿其整个生命周期,是人的勇气来排除困境,让团队把局部的最优抛之脑后,达到更重大的目标。表明了XP对“人让项目取得成功”的基本信任态度。

一项实践在XP环境中成功使用的依据通过XP的法则呈现,包括:快速反馈、假设简单性、递增更改、提倡更改、优质工作。

XP软件开发的基石是XP的活动,包括:编码、测试、倾听、设计。

(二)编码标准

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

编码标准中的版式就是一个很好的例子,版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要因素。

三)结对编程

结对编程是XP中的重要实践。在结对编程模式下,一对程序员肩并肩、平等地、互补地进行开发工作。他们并排坐在一台电脑前,面对同一个显示器,使用同一个键盘、同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起做单元测试,一起做集成测试,一起写文档等。 结对编程中有两个角色:

驾驶员(Driver)是控制键盘输入的人。

领航员(Navigator)起到领航、提醒的作用。

如何结对编程,为何要结对编程,大家参考一下结对编程和两人合作,重点是:

驾驶员:写设计文档,进行编码和单元测试等XP开发流程。

领航员:审阅驾驶员的文档、驾驶员对编码等开发流程的执行;考虑单元测试的覆盖率;思考是否需要和如何重构;帮助驾驶员解决具体的技术问题。

驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。

主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。没有“我的代码”、“你的代码”或“他/她的代码”,只有“我们的代码”。

只有水平上的差距,没有级别上的差异。两人结对,尽管可能大家的级别资历不同,但不管在分析、设计或编码上,双方都拥有平等的决策权利。

团队精神是好多地方都强调的一个精神,最小的团队就是一对一的二人团队了,培养团队精神从结对编程开始吧。社会生活中人与人相处最重要的是诚信,有同理心,互利。结对编程中大家会出现分歧,如何更有效地合作要做到对事不对人,掌握这些是可以终生受益的。

(四)版本控制

XP的集体所有制意味着每个人都对所有的代码负责;这一点,反过来又意味着每个人都可以更改代码的任意部分。结对编程对这一实践贡献良多:借由在不同的结对中工作,所有的程序员都能看到完全的代码。集体所有制的一个主要优势是提升了开发程序的速度,因为一旦代码中出现错误,任何程序员都能修正它。 这意味着代码要放到一个大家都能方便获取的地方,我们叫代码仓库。这引出另外一个话题叫版本控制(Version Control)。

不论是对于团队还是个体,版本控制都提供了很多好处。

版本控制提供项目级的 undo(撤销) 功能: 没有什么事情是终结版本, 任何错误必须很容易回滚。 假设你在使用世界上最复杂的文字处理系统。 它具备了所有的能想到的功能,就是没有支持 DELETE(删除) 键。想象你打字的时候得多么的谨慎和缓慢吧, 特别是一篇超大的文档的快临近末尾的时候, 一个不小心就要重头再来(试想你选中所有的文字, 不小心按了 DELETE 键, 因为没有撤销功能,只好重新录入)。编辑文字和版本控制相同,任何时候都需要回滚,无论是一个小时, 一天, 还是一周, 这让你的团队工作自由快速的工作, 而且对于修正错误也非常自信。

版本控制允许多人在同一代码上工作, 只要遵守一定的控制原则就行。 再也不会发生诸如一个人覆盖了另一个人编辑的代码,导致那个人的修改无效这样的情况。

版本控制系统保存了过去所作的修改的历史记录。如果你遭遇到一些惊讶的代码,通过版本控制系统可以很容易找出是谁干的, 修改了什么, 修改的时间, 如果幸运的话,还能找出原因。

版本控制系统还支持在主线上开发的同时发布多个软件版本。在软件发布的时候也不需要整个团队的停止工作,不需要冻结代码。

版本控制也是项目级的时间机器,你可以选择任何一个时间, 精确地查看项目在当时的情况。 这对研究非常有用, 也是重现以前某个有问题的发布版本的基础

(五)重构

我们先看看重构的概念:

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

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

(六)实验过程截图:

1,Eclipse菜单中的source->Format 或用快捷键Ctrl+Shift+F就可以按Eclipse规定的规范缩进

2、重构

重构的一个具体实例:

3、结对编程

游戏名称:玩转五子棋

队友:20159215田仁贵

博客:http://www.cnblogs.com/20159215trg/

我的主要任务是:编写伪代码、完善代码、添加注释、TDD测试

/*

五子棋主框架,程序启动

*/

public class StartChessJFrame extends JFrame {

private ChessBoard chessBoard;

private JPanel toolbar;

private JButton startButton, backButton, exitButton;

private JMenuBar menuBar;

private JMenu sysMenu;

private JMenuItem startMenuItem, exitMenuItem, backMenuItem;

// 重新开始,退出,和悔棋菜单项

public StartChessJFrame() {

setTitle("单机版五子棋");// 设置标题

chessBoard = new ChessBoard();

Container contentPane = getContentPane();

contentPane.add(chessBoard);

chessBoard.setOpaque(true);

// 创建和添加菜单

menuBar = new JMenuBar();// 初始化菜单栏

sysMenu = new JMenu("系统");// 初始化菜单

// 初始化菜单项

startMenuItem = new JMenuItem("重新开始");

exitMenuItem = new JMenuItem("退出");

backMenuItem = new JMenuItem("悔棋");

// 将三个菜单项添加到菜单上

sysMenu.add(startMenuItem);

sysMenu.add(exitMenuItem);

sysMenu.add(backMenuItem);

// 初始化按钮事件监听器内部类

MyItemListener lis = new MyItemListener();

// 将三个菜单注册到事件监听器上

this.startMenuItem.addActionListener(lis);

backMenuItem.addActionListener(lis);

exitMenuItem.addActionListener(lis);

menuBar.add(sysMenu);// 将系统菜单添加到菜单栏上

setJMenuBar(menuBar);// 将menuBar设置为菜单栏

toolbar = new JPanel();// 工具面板实例化

// 三个按钮初始化

startButton = new JButton("重新开始");

exitButton = new JButton("退出");

backButton = new JButton("悔棋");

// 将工具面板按钮用FlowLayout布局

toolbar.setLayout(new FlowLayout(FlowLayout.LEFT));

// 将三个按钮添加到工具面板

toolbar.add(startButton);

toolbar.add(exitButton);

toolbar.add(backButton);

// 将三个按钮注册监听事件

startButton.addActionListener(lis);

exitButton.addActionListener(lis);

backButton.addActionListener(lis);

// 将工具面板布局到界面下方

add(toolbar, BorderLayout.SOUTH);

add(chessBoard);// 将面板对象添加到窗体上

// 设置界面关闭事件

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// setSize(800,800);

pack();// 自适应大小

}

private class MyItemListener implements ActionListener {

public void actionPerformed(ActionEvent e) {

Object obj = e.getSource();// 获得事件源

if (obj == StartChessJFrame.this.startMenuItem

|| obj == startButton) {

// 重新开始

// JFiveFrame.this内部类引用外部类

System.out.println("重新开始");

chessBoard.restartGame();

} else if (obj == exitMenuItem || obj == exitButton)

System.exit(0);

else if (obj == backMenuItem || obj == backButton) {

System.out.println("悔棋...");

chessBoard.goback();

}

}

}

public static void main(String[] args) {

StartChessJFrame f = new StartChessJFrame();// 创建主框架

f.setVisible(true);// 显示主框架

}

}

/**

* 棋子类

*/

public class Point {

private int x;// 棋盘中的x索引

private int y;// 棋盘中的y索引

private Color color;// 颜色

public static final int DIAMETER = 30;// 直径

public Point(int x, int y, Color color) {

this.x = x;

this.y = y;

this.color = color;

}

public int getX() {// 拿到棋盘中x的索引

return x;

}

public int getY() {

return y;

}

public Color getColor() {// 获得棋子的颜色

return color;

}

}

/**

* 五子棋--棋盘类

*/

public class ChessBoard extends JPanel implements MouseListener {

public static final int MARGIN = 30;// 边距

public static final int GRID_SPAN = 35;// 网格间距

public static final int ROWS = 15;// 棋盘行数

public static final int COLS = 15;// 棋盘列数

Point[] chessList = new Point[(ROWS + 1) * (COLS + 1)];// 初始每个数组元素为null

boolean isBlack = true;// 默认开始是黑棋先

boolean gameOver = false;// 游戏是否结束

int chessCount;// 当前棋盘棋子的个数

int xIndex, yIndex;// 当前刚下棋子的索引

Image img;

Image shadows;

Color colortemp;

public ChessBoard() {

setBackground(Color.orange);// 设置背景色为橘黄色

img = Toolkit.getDefaultToolkit().getImage("board.jpg");

shadows = Toolkit.getDefaultToolkit().getImage("shadows.jpg");

addMouseListener(this);

addMouseMotionListener(new MouseMotionListener() {

public void mouseDragged(MouseEvent e) {

}

public void mouseMoved(MouseEvent e) {

// 边距 网格间距

int x1 = (e.getX() - MARGIN + GRID_SPAN / 2) / GRID_SPAN;

// 将鼠标点击的坐标位置转成网格索引

int y1 = (e.getY() - MARGIN + GRID_SPAN / 2) / GRID_SPAN;

// 游戏已经结束不能下

// 落在棋盘外不能下

// x,y位置已经有棋子存在,不能下

if (x1 < 0 || x1 > ROWS || y1 < 0 || y1 > COLS || gameOver

|| findChess(x1, y1))

setCursor(new Cursor(Cursor.DEFAULT_CURSOR));

// 设置成默认状态

else

setCursor(new Cursor(Cursor.HAND_CURSOR));

}

});

}

// 绘制

public void paintComponent(Graphics g) {

super.paintComponent(g);// 画棋盘

int imgWidth = img.getWidth(this);

int imgHeight = img.getHeight(this);// 获得图片的宽度与高度

int FWidth = getWidth();

int FHeight = getHeight();// 获得窗口的宽度与高度

int x = (FWidth - imgWidth) / 2;

int y = (FHeight - imgHeight) / 2;

g.drawImage(img, x, y, null);

for (int i = 0; i <= ROWS; i++) {// 画横线

g.drawLine(MARGIN, MARGIN + i * GRID_SPAN, MARGIN + COLS

* GRID_SPAN, MARGIN + i * GRID_SPAN);

}

for (int i = 0; i <= COLS; i++) {// 画竖线

g.drawLine(MARGIN + i * GRID_SPAN, MARGIN, MARGIN + i * GRID_SPAN,

MARGIN + ROWS * GRID_SPAN);

}

// 画棋子

for (int i = 0; i < chessCount; i++) {

// 网格交叉点x,y坐标

int xPos = chessList[i].getY() * GRID_SPAN + MARGIN;

int yPos = chessList[i].getX() * GRID_SPAN + MARGIN;

g.setColor(chessList[i].getColor());// 设置颜色

// g.fillOval(xPos-Point.DIAMETER/2, yPos-Point.DIAMETER/2,

// Point.DIAMETER, Point.DIAMETER);

// g.drawImage(shadows, xPos-Point.DIAMETER/2,

// yPos-Point.DIAMETER/2, Point.DIAMETER, Point.DIAMETER, null);

colortemp = chessList[i].getColor();

if (colortemp == Color.black) {

RadialGradientPaint paint = new RadialGradientPaint(xPos

- Point.DIAMETER / 2 + 25, yPos - Point.DIAMETER / 2

+ 10, 20, new float[] { 0f, 1f }, new Color[] {

Color.WHITE, Color.BLACK });

((Graphics2D) g).setPaint(paint);

((Graphics2D) g).setRenderingHint(

RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

((Graphics2D) g).setRenderingHint(

RenderingHints.KEY_ALPHA_INTERPOLATION,

RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);

} else if (colortemp == Color.white) {

RadialGradientPaint paint = new RadialGradientPaint(xPos

- Point.DIAMETER / 2 + 25, yPos - Point.DIAMETER / 2

+ 10, 70, new float[] { 0f, 1f }, new Color[] {

Color.WHITE, Color.BLACK });

((Graphics2D) g).setPaint(paint);

((Graphics2D) g).setRenderingHint(

RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

((Graphics2D) g).setRenderingHint(

RenderingHints.KEY_ALPHA_INTERPOLATION,

RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);

}

Ellipse2D e = new Ellipse2D.Float(xPos - Point.DIAMETER / 2, yPos

- Point.DIAMETER / 2, 34, 35);

((Graphics2D) g).fill(e);

// 标记最后一个棋子的红矩形框

if (i == chessCount - 1) {// 如果是最后一个棋子

g.setColor(Color.red);

g.drawRect(xPos - Point.DIAMETER / 2,

yPos - Point.DIAMETER / 2, 34, 35);

}

}

}

public void mousePressed(MouseEvent e) {// 鼠标在组件上按下时调用

// 游戏结束时,不再能下

if (gameOver)

return;

String colorName = isBlack ? "黑棋" : "白棋";

// 将鼠标点击的坐标位置转换成网格索引

xIndex = (e.getX() - MARGIN + GRID_SPAN / 2) / GRID_SPAN;

yIndex = (e.getY() - MARGIN + GRID_SPAN / 2) / GRID_SPAN;

// 落在棋盘外不能下

if (xIndex < 0 || xIndex > ROWS || yIndex < 0 || yIndex > COLS)

return;

// 如果x,y位置已经有棋子存在,不能下

if (findChess(xIndex, yIndex))

return;

// 可以进行时的处理

Point ch = new Point(xIndex, yIndex, isBlack ? Color.black

: Color.white);

chessList[chessCount++] = ch;

repaint();// 通知系统重新绘制

// 如果胜出则给出提示信息,不能继续下棋

if (isWin()) {

String msg = String.format("恭喜,%s赢了!", colorName);

JOptionPane.showMessageDialog(this, msg);

gameOver = true;

}

isBlack = !isBlack;

}

// 覆盖mouseListener的方法

public void mouseClicked(MouseEvent e) {

// 鼠标按键在组件上单击时调用

}

public void mouseEntered(MouseEvent e) {

// 鼠标进入到组件上时调用

}

public void mouseExited(MouseEvent e) {

// 鼠标离开组件时调用

}

public void mouseReleased(MouseEvent e) {

// 鼠标按钮在组件上释放时调用

}

// 在棋子数组中查找是否有索引为x,y的棋子存在

private boolean findChess(int x, int y) {

for (Point c : chessList) {

if (c != null && c.getX() == x && c.getY() == y)

return true;

}

return false;

}

private boolean isWin() {

int continueCount = 1;// 连续棋子的个数

// 横向向西寻找

for (int x = xIndex - 1; x >= 0; x--) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, yIndex, c) != null) {

continueCount++;

} else

break;

}

// 横向向东寻找

for (int x = xIndex + 1; x <= COLS; x++) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, yIndex, c) != null) {

continueCount++;

} else

break;

}

if (continueCount >= 5) {

return true;

} else

continueCount = 1;

// 继续另一种搜索纵向

// 向上搜索

for (int y = yIndex - 1; y >= 0; y--) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(xIndex, y, c) != null) {

continueCount++;

} else

break;

}

// 纵向向下寻找

for (int y = yIndex + 1; y <= ROWS; y++) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(xIndex, y, c) != null)

continueCount++;

else

break;

}

if (continueCount >= 5)

return true;

else

continueCount = 1;

// 继续另一种情况的搜索:斜向

// 东北寻找

for (int x = xIndex + 1, y = yIndex - 1; y >= 0 && x <= COLS; x++, y--) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, y, c) != null) {

continueCount++;

} else

break;

}

// 西南寻找

for (int x = xIndex - 1, y = yIndex + 1; x >= 0 && y <= ROWS; x--, y++) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, y, c) != null) {

continueCount++;

} else

break;

}

if (continueCount >= 5)

return true;

else

continueCount = 1;

// 继续另一种情况的搜索:斜向

// 西北寻找

for (int x = xIndex - 1, y = yIndex - 1; x >= 0 && y >= 0; x--, y--) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, y, c) != null)

continueCount++;

else

break;

}

// 东南寻找

for (int x = xIndex + 1, y = yIndex + 1; x <= COLS && y <= ROWS; x++, y++) {

Color c = isBlack ? Color.black : Color.white;

if (getChess(x, y, c) != null)

continueCount++;

else

break;

}

if (continueCount >= 5)

return true;

else

continueCount = 1;

return false;

}

private Point getChess(int xIndex, int yIndex, Color color) {

for (Point p : chessList) {

if (p != null && p.getX() == xIndex && p.getY() == yIndex

&& p.getColor() == color)

return p;

}

return null;

}

public void restartGame() {

// 清除棋子

for (int i = 0; i < chessList.length; i++) {

chessList[i] = null;

}

// 恢复游戏相关的变量值

isBlack = true;

gameOver = false; // 游戏是否结束

chessCount = 0; // 当前棋盘棋子个数

repaint();

}

// 悔棋

public void goback() {

if (chessCount == 0)

return;

chessList[chessCount - 1] = null;

chessCount--;

if (chessCount > 0) {

xIndex = chessList[chessCount - 1].getX();

yIndex = chessList[chessCount - 1].getY();

}

isBlack = !isBlack;

repaint();

}

// 矩形Dimension

public Dimension getPreferredSize() {

return new Dimension(MARGIN * 2 + GRID_SPAN * COLS, MARGIN * 2

+ GRID_SPAN * ROWS);

}

}

实验时间

步骤 耗时 百分比
需求分析 50min 27%
设计 40min 21.6%
代码实现 50min 27%
测试 45min 24.3%
分析总结 40min 21.6%

实验总结

通过这次实验我发现自己的编程能力不足,主要是基础知识薄弱,在编写StudentTest类时,不会使用toString()方法,我认真研究了toString()方法后,还是没有解决,最后换了个思路将代码重新查了一遍后发现原来是一个“}”的问题,使得static显示错误,我的查错能力有待提高,很多错误都是很简单却很难发现的,在今后的学习中要着重提高这方面的能力。

因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。

后续会有更多的精彩的内容分享给大家。