OOpre_Homework2
第二次作业就算开始面向对象的入门啦
我也不会写什么很硬核的东西hh
如果你不幸点进来了随便看看就好了
作业要求回顾
- 学会构建构造方法
- 理解封装的作用
- 学习简单容器的使用
- 学习git的基础知识
有关git的内容我会单开一个文章集合,这里就不涉及啦~
题目背景
我是一个穿越到魔法大陆上的冒险者,在旅途中我们需要收集各种道具,使用各种装备,招募其他冒险者加入队伍,提升自己的攻击力和防御力并体验各种战斗。
实践
构建构造
- 基本数据类型: 类似C语言
- 复合数据类型:使用
new1
Bottle bottle = new Bottle(id,value);
简单容器
ArraylList是类似于C语言中数组的一个存在,但更加强大,用起来也更加方便。考虑到后面要用到相关知识,所以我们把这个内容提前到前面来。
ArrayList类位于 java.util 包中,使用前需要引入1
import java.util.ArrayList;
接下来,我们以Bottle类(有两个基本属性,botId和value)为例,来讲解ArrayList的一些基本用法。
- 创建容器
1
ArrayList<Bottle> bottles = new ArrayList<>();
- 加入元素
1
Bottle bot1 = new Bottle("HealPotion", 50);
- 删除元素
1
2bottles.remove(bot1);//从容其中删除bot1
bottles.remove(i);//删除容器中下标为i元素 - 判断元素是否在容器中
1
boolen in = bottles.contains(bottle);
- 访问下标为
i的元素1
Bottle bottle = bottles.get(i);
- 容器的大小
1
int size = bottles.size();
- 遍历容器中的元素
1
2
3for (Bottle item : bottles){
//do something
}
对类进行封装
在本题中,我们需要定义的类有
- Adventure (ID)
- Bottle (ID,effect)
- Equipment (ID)
当然,这只是我们在阅读题目后对这三个类的一个初步构想。考虑到每个冒险者都有自己的装备库 和药水库,所以我们给Adventure增加两个属性(BotList和EquipList)。
在确定属性之后,我们要考虑的就是构建什么样的方法。
在本题中,我们涉及到以下操作:
- 添加
Adventure 为
Adventure添加/删除bottle删除后要输出
Adventure剩余的bottle数量,以及删除的bottle的value为
Adventure添加/删除equipment删除后要输出
Adventure剩余的equipment数量
这时候我们会有一个自然的想法(也可能只有我有好吧),
我们添加/删除bottle的方法能否放在bottle类里呢?
添加/删除bottle的操作显然和bottle有关,
并且类似的添加/删除操作还有equipment,如果这些方法都放在Adventure类里是不是太挤了呢?
而放在各自的类里显然会更加简洁清晰。
但是,这是不能的。(也可能可以好吧,但作者目前知识有限,多多包容~)
因为添加/删除这些操作的对象表面上是bottle,
但实际上是包含bottle的容器,也就是bottleArrayList。
在bottle类里的操作对象显然是元素,而不是容器。
而只有在Adventure类里可以对Adventure的属性bottleArrayList(也就是一个容器)进行操作。
那么理清楚这个关系之后我们就可以设计好这三个类以及Main类里需要什么方法了。
Main(注意这里的方法定义要写static)- 添加
Adventure接口设置为(
Adventure)
接口还要有AdventureList,因为方法在运行函数外面,而在运行函数里定义的量相当于C语言里的局部变量。
这样函数里只有一行add操作,实在是太easy啦!
- 添加
Adventure- 添加/删除
bottle接口设置为(bottle) - 添加/删除
Equipment接口设置为(Equipment)
- 添加/删除
Bottle- 基础功能:
getID()直接return id;getValue()直接return value;
- 基础功能:
Equipment- 基础功能:
getID()直接return id;
- 基础功能:
有了这些之后,我们就可以开始撰写Main也就是主函数的内容了。
首先是要对输入进行解析(这里直接套用第二次作业的介绍里给的范例):1
2
3
4
5
6
7
8ArrayList<ArrayList<String>> inputInfo = new ArrayList<>(); // 解析后的输入将会存进该容器中, 类似于c语言的二维数组
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine().trim()); // 读取行数
for (int i = 0; i < n; ++i) {
String nextLine = scanner.nextLine(); // 读取本行指令
String[] strings = nextLine.trim().split(" +"); // 按空格对行进行分割
inputInfo.add(new ArrayList<>(Arrays.asList(strings))); // 将指令分割后的各个部分存进容器中
}
如果输入:1
2
3
4
5
6
76
aa Alice
ab Alice HealPotion 50
ab Alice ManaPotion 100
ae Alice IronSword
rb Alice HealPotion
re Alice IronSword
这时候我们的指令就被存在二维数组里了,相当于:1
2
3
4
5
6
7
8inputInfo[][]={
"aa" ,"Alice";
"ab" ,"Alice" ,"HealPotion" ,"50";
"ab" ,"Alice" ,"ManaPotion" ,"100";
"ae" ,"Alice" ,"IronSword";
"rb" ,"Alice" ,"HealPotion";
"re" ,"Alice" ,"IronSword";
}
这时候我们就可以根据解析后的命令来操作了。
这里给出一个基本的框架:1
2
3
4
5
6
7
8
9
10
11
12for (ArrayList<String> instruction : inputInfo) {
String command = instruction.get(0);
switch (command) {
case "aa": {
//do something
break;
}
/*省略其它命令*/
default:
break;
}
}
而处理命令时我们有一个基本的流程,
这里以ae Alice IronSword为例,
先在储存Adventure容器中找到Alice,
然后找到它的Equipment容器,
再调用函数进行操作。
这样就保证了我们确实将IronSword
添加到Alice的装备库里了,
而不是添加到某个不知道在哪的Alice的装备库里。
这时候我们就发现我们需要添加几个根据Id
在容器里找到要操作元素的函数:
MAin:getAdventure
Adventure:getBottlegetEquipment
使用for循环遍历容器,注意比较Id,
找到后返回相应的元素,否则返回null
当我们顺利地完成之后,会发现主函数过于臃肿了。
以rb移除药水瓶为例:1
2
3
4
5
6
7
8
9
10
11case "rb": {
String adv = instruction.get(1);
String botId = instruction.get(2);
Adventure adventure = Main.GetAdventure(advArrayList,adv);
Bottle bottle = adventure.GetBottle(botId);
if (bottle != null) {
adventure.RemoveBottle(botId);
System.out.println(adventure.GetBottleNum() + " " + bottle.getEffect());
}
break;
}
而方法里只有一行!:1
2
3public void RemoveBottle(Bottle bottle) {
botArList.remove(bottle);
}
这不符合我们要求主函数尽量简洁的原则,同时这个方法似乎也没有存在的必要。
因此我们修改一下接口,将寻找的过程也放在方法里,同时将找到要删除的bottle后的过程提取出来变成方法(主要是删除和输出部分)。
这样修正后的代码就是这样!
是不是减少了很多!1
2
3
4
5
6
7
8case "rb": {
String adv = instruction.get(1);
String botId = instruction.get(2);
Adventure adventure = Main.GetAdventure(advArrayList,adv);
Main.printBot(adventure,botId);
//这里更进一步,也可以将找adventure的代码放到print里,但是作者当时没有发现hh
break;
}
这是优化后的remove函数:1
2
3
4
5
6public void RemoveBottle(String botId) {
Bottle bottle = GetBottle(botId);
if (bottle != null) {
botArList.remove(bottle);
}
}
这是提取出来的print函数:1
2
3
4
5
6
7private static void printBot(Adventure adventure,String botId) {
if (adventure != null) {
Bottle bottle = adventure.GetBottle(botId);
adventure.RemoveBottle(botId);
System.out.println(adventure.GetBottleNum() + " " + bottle.getEffect());
}
}-
根据这个思想,我们对剩下的方法接口也进行一些完善:
Main- 添加
Adventure(AdventureList,adventureID)
- 添加
Adventure- 添加/删除
bottle(bottleId,bottleValue) - 添加/删除
Equipment(EquipmentId)
这样主函数也更加简洁了!
- 添加/删除
运行之后发现没有什么问题!
至此我们的程序设计就告一段落。
接下来就是应用Junit来进行测试。


