休闲时刻 - 打造 CMD 战舰(三)
文章来源:《Head First Java》修炼感悟。
上一篇,老白通过「硬编码」对战舰类的功能模块进行了简单测试。 本篇的主要任务是继续完善战舰类并反复测试,其中涉及到几个新技术,师兄们如果感兴趣,请接着往下看。
项目进度:
- 列出必要的程序清单
- 简单描述核心功能的实现方式
- 根据程序清单写出空类、空方法(不要求具体实现)
- 编写测试代码,确保功能模块之间正常调用
- 编写战舰类方法的具体实现,测试+修改
- 编写玩家类方法实现,测试+修改
- 整理代码,关键位置加入注释
任务五、完善战舰类功能模块
不要忘记,Warship
类中的 setCellsLocation
、returnFireResult
两个方法还没有任何代码,现在就赋予它们「超能力」。
1、实现方法 setCellsLocation()
只做一件事,就是设置自身方位。 上一篇中老白说过,这个方法会传入一个 int
数组。 不要考虑是谁传进来的(很有可能是电脑随机生成的),只考虑要怎样接收这个参数。 其实很简单,先声明一个私有变量 cellsLocation
,然后把传进来的参数赋值给 cellsLocation
就可以了,就像这样:
// 用来保存战舰格子的方位数组
private int[] cellsLocation;
// 设置战舰格子的当前方位
public void setCellsLocation(int[] locs) {
this.cellsLocation = locs;
}
2、实现方法 returnFireResult()
这个方法稍微有点复杂,老白把关注点列出来,然后逐条解释:
- 需要对传入的
String
类型参数进行转换; - 需要实现一个循环,把自己的方位格子与传进来的猜测值逐个比较;
- 如果检测出被击中,还需要进行二次检测,判断是否被 kill;
- 需要返回本次攻击效果。
看看代码是如何实现的:
// 检查当前格子是否被击中,并返回信息
public String returnFireResult(String hitLoc) {
// 把传入的字符串解析为整型数字,不要过问如何解析的
int loc = Integer.parseInt(hitLoc);
// 用于保存每次攻击的效果,默认为「未击中」
String result = "miss";
// 自身所有格子与玩家猜测的位置逐个进行比较,
// 如果某个格子与玩家猜测值相等,则表示那个格子被玩家击中,
// 生命值就会被减掉1点,然后直接中断循环,放弃后面的比较。
for (int cell : cellsLocation) {
if (loc == cell) {
result = "hit"; // 击中
hitPoints--; // 减掉1点生命值
break; // 中断循环
}
}
// 判断是否被击杀
if (hitPoints < 1) {
result = "kill";
}
// 返回本次攻击结果
System.out.println(result);
return result;
}
2.1、解析字符串 Integer.parseInt()
int loc = Integer.parseInt(hitLoc);
上面这条语句,我在注释中提到「不要过问怎么解析的」,因为 parseInt()
是 Java 内建的 Integer
类提供的一个静态方法。 只需知道它的作用是「把参数指定的字符串转换为整型值」就可以了,具体怎样转换的你无需关注。 在我们这个项目中,参数 hitLoc
是玩家输入的猜测值(类型为 String
),经过解析后转换为 int
值赋给 loc
。
2.2、加强版 for 循环
紧接着是一个加强版的 for
循环,老白在前面提过,现在来详细说说。
正常情况下,我们使用的都是普通的 for
循环,比如这种形式 for(int i=0;i<5;i++){}
。 从 Java1.5 开始新增了加强版的 for
循环,就像代码中使用的这样:
for (int cell : cellsLocation) {
// 进入循环体,你可以「随心所欲」地去处理 cell
}
这种 for
循环多用于引用类型数组或集合,处理数组中的对象元素很方便。 就像注释中说的,给老白的感觉就是「随心所欲」。 它会把数组中的每一个元素都迭代一次,让你有机会对每个元素都做点什么。 目前我们正在做的是把玩家猜测值与战舰自身的每个格子都比较一次:
for (int cell : cellsLocation) {
if (loc == cell) {
// 能够执行到 if 中,说明战舰已经被击中
result = "hit"; // 把返回结果标记为「击中」
hitPoints--; // 减掉1点生命值
break; // 中断循环
}
}
注意 :
hitPoint--
,这种代码称为「自减」,等效为hitPoint = hitPoint-1
;break
,表示中断循环,直接跳出循环体外,继续执行下一条语句。
代码中,如果检测到玩家猜测值与战舰位置一致,那么直接中断for
循环,也没有必要继续检查余下的几个位置。 有没有感觉提高了一点点执行效率?
2.3、Kill 检查
这个判断语句很好理解,检查生命点数是否小于 1。 小于 1 意味着 0 或者以下,可以直接判定 Kill 了。
// 检查 hitPoint 点数
if (hitPoints < 1) {
result = "kill"; // 把返回结果标记为「击杀」
}
2.4、返回打击效果
返回语句 return
没什么好说的,就是返回 hit、miss、kill 其中的一个。 必须要返回,因为方法声明时已经明确表示要返回一个 String
值。 这个返回值可以有很多用处,老白就很喜欢使用打印语句返回点信息。 如果在测试期间调用某个方法后没有任何反应,感觉心里不踏实。
3、功能模块测试
以上工作完成后,我们再来测试一次。 因为上一篇已经检验过测试类,各功能模块配合良好,所以不会担心代码结构出现问题。 只要不随便改动变量名、方法名,那么允许你在方法中「肆意妄为」。
这次只是在方法中动了「手脚」,所以测试类不需要任何改动。 Warship.java
重新编译一次,然后执行:
把测试类中玩家的猜测值改为其它值再试一次(测试类需要重新编译):
// 模拟玩家猜测值
String guess = "5";
Perfect! 程序准确地识别出本次攻击结果为「miss」。
是不是感觉小有成就? 但每次总是这样改动猜测值,确实有点痛苦。 下一篇我们将实现玩家辅助类,通过键盘输入猜测值,从此再无「烦恼」。
《 上一篇 休闲时刻 - 打造 CMD 战舰(二) |
---|