游戏背景

在这个架空的魔法世界中,玩家将扮演一位冒险者,探索一个充满挑战和机遇的魔法大陆。玩家需要管理自己的资源(放入背包或仓库),包括药水瓶、装备,并可以用碎片兑换福利。在收集装备、药水瓶和福利碎片,雇佣冒险者的过程中,玩家将增强自己的实力,并通过普通攻击或者递归攻击击败其他冒险者。被雇佣的冒险者将会在雇主减少某种程度的体力值时给予援助。在冒险的最后,冒险者们将进入“秘境工厂”打败守门怪物,寻找宝藏。

架构设计

最终架构

经过多次迭代开发,已经能够完整实现游戏功能,并具有较为清晰的架构设计。
UML类图

核心游戏接口和类

类/接口 功能
Adventurer 代表游戏中的冒险者,包含属性如ID、名称、体力、攻击力、防御力等
Item 基类,代表所有物品,包括药水瓶Bottle和装备Equipment,包含属性有ID、名称、CE、是否被携带等
Bottle 药水瓶基类,具体子类包括HpBottle(体力药水)、AtkBottle(攻击药水)、DefBottle(防御药水)
Equipment 装备类,包括装备类型、耐久度等属性
Guard 守卫接口,定义了战斗逻辑,具体实现类如ShdFlmStnWndFrz
Treasure 宝物接口,定义了宝物信息的显示与宝物的使用,具体实现类如ShdTreasureFlmTreasure
Fragment 碎片类,用于收集和兑换福利
CommandUtil 命令接口,用于处理用户输入的命令,具体实现类包括AddAdventurersAddBottlesAddEquipmentsAddDurabilitiesTakeItemsUseBottlesAddFragmentsUseFragmentsFightEmployAdventureGame
InputManage 输入管理类,用于解析用户输入并调用相应的命令处理类
TreasureFactory 工厂类,用于创建不同类型的宝物
MainClass 游戏入口类,读取用户输入并启动主循环

在具体实现过程中,采用了面向对象的设计原则,通过继承、多态等特性实现代码的复用和扩展。同时,通过模块化设计,将不同功能需求划分为独立的模块,并在对应的类中实现,提高了代码的可维护性和可扩展性。

代码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
├── project
│ ├── src
│ │ ├── Adventurer.java // 代表游戏中的冒险者,包含属性如ID、名称、体力、攻击力、防御力等
│ │ ├── Item.java // 基类,代表所有物品
│ │ ├── Bottle.java // 药水瓶基类
│ │ ├── HpBottle.java // 体力药水瓶
│ │ ├── AtkBottle.java // 攻击药水瓶
│ │ ├── DefBottle.java // 防御药水瓶
│ │ ├── Equipment.java // 装备类,包括装备类型、耐久度等属性
│ │ ├── Fragment.java // 碎片类,用于收集和兑换福利
│ │ ├── Guard.java // 守卫接口
│ │ ├── ··· // 实现类Shd、Flm、Stn、Wnd、Frz
│ │ ├── TreasureFactory.java // 工厂类,用于创建不同类型的宝物
│ │ ├── Treasure.java // 宝物接口
│ │ ├── ··· // 实现类ShdTreasure、FlmTreasure、StnTreasure、WndTreasure、FrzTreasure
│ │ ├── CommandUtil.java // 命令接口,用于处理用户输入的命令
│ │ ├── AddAdventurers.java // 添加冒险者的命令处理类
│ │ ├── AddBottles.java // 添加药水瓶的命令处理类
│ │ ├── AddEquipments.java // 添加装备的命令处理类
│ │ ├── AddDurabilities.java // 添加耐久度的命令处理类
│ │ ├── TakeItems.java // 拿起物品的命令处理类
│ │ ├── UseBottles.java // 使用药水瓶的命令处理类
│ │ ├── AddFragments.java // 添加碎片的命令处理类
│ │ ├── UseFragments.java // 使用碎片的命令处理类
│ │ ├── Fight.java // 战斗的命令处理类
│ │ ├── Employ.java // 雇佣的命令处理类
│ │ ├── AdventureGame.java // 冒险游戏流程的命令处理类
│ │ ├── InputManage.java // 输入管理类
│ │ └── MainClass.java // 游戏入口类
│ │
│ └── test
│ ├── AdventurerTest.java // 冒险者类的单元测试
│ ├── BottleTest.java // 药水瓶类的单元测试
│ ├── EquipmentTest.java // 装备类的单元测试
│ ├── TreasureTest.java // 宝物接口的单元测试
│ ├── ··· // 单元测试
│ ├── CommandUtilTest.java // 命令接口的单元测试
│ ├── InputManageTest.java // 输入管理类的单元测试
│ └── TreasureFactoryTest.java // 宝物工厂类的单元测试

迭代中的架构调整

代码重构

  1. 在第三次作业时,将输入的管理和具体命令执行分离,在InputManage类中处理输入,通过switch-case语句进行命令的判断与执行,并在具体类中实现对应功能。

此次代码重构精简了MainClass类,使得代码更加清晰,易于维护。同时将每种命令处理封装在对应的方法中,提高了模块化程度。

  1. 在第五次作业中,通过创建CommandUtil接口,并创建多个具体的命令执行类,每个实现类对应一种命令。同时,在InputManage类中使用Arraylist储存命令,通过get方法获取并执行对应命令。

此次代码重构进一步提高了代码的模块化程度,使得代码更加清晰,易于维护。同时,通过使用接口,使得代码易于扩展,便于增加新的命令。

增量开发

每次作业中,都需要增加新的命令,通过采用层级结构,在对应的类中实现相应的方法,从而将一个较为复杂的命令简化为多个简单方法的层次组合,使得代码更加清晰,易于维护。

使用JUnit的心得体会

JUnit测试的使用可以帮助检测代码中潜在的错误,提高了因从整体层面构造样例导致部分语句和分支未被覆盖而导致的bug,同时,通过对每个类中的方法编写测试用例,可以更深入地理解其逻辑与功能,自下而上地提高整个项目代码的可靠性和质量。
但在编写JUnit测试代码时,也产生了一些问题。例如,在进行测试时,由于封装导致部分方法在测试时无法直接获得对象的某些属性,需要通过添加get方法获取对应属性。另外,由于增量开发需要在原有代码基础上增加对象的属性,因而测试代码也需要进行相应的修改,增加了测试代码的维护成本。

学习OOPre的心得体会

在从面向过程编程过渡到面向对象编程的过程中,我形成了更严谨的层次化、模块化意识。在程序设计与数据结构课上,我虽然也刻意培养了模块意识,尽可能简化主函数,并减少不同函数间代码的复用与冗余部分,但通过OOPre的学习,我更加深刻地理解了面向对象编程的精髓,即通过封装、继承、多态等特性,将复杂问题分解为多个简单问题,从而提高代码的可维护性和可扩展性。而层次化模块化的设计使得我们可以在不考虑具体实现的情况下,优先从整体角度搭建一个较为清晰的框架,而在更低的层次中完成具体代码逻辑与功能的实现。
另外,OOPre的课程要求帮助我更深刻地认识到做好本地测试的重要性,不论是使用JUnit进行单元测试还是本地搭建评测机和编写样例,都有助于我们更早地发现并修复代码中的错误,帮助我们更好地debug。

课程建议

  1. 或许可以在更早的时间就提高测试数据点的强度,帮助同学们及早发现代码中的问题。当然要是希望把生成数据和编写本地评测机作为课程的一部分,在后期提高测试点强度也是合理的