文章目录
前情提要
在这片土地上,你是一场冒险游戏的掌控者,你需要管理着一群冒险者,还有他们的药瓶、装备与食物,他们有各自的物品仓库,也会有随身携带的背包,他们之间会有战斗,也会有相互的帮助。同时,你还是一个商店的经营者,收购并出售各式物品。
博客链接:https://swkfk.top/jump/oopre-hw8
代码仓库:https://github.com/swkfk/BUAA-2023-OOPre-Code(可以查阅全部提交信息)
一、结构设计
经过 5 次的开发与迭代后,代码已经初具规模,并具备了一定的结构。
接口与类的分类与调用关系
按照功能分类与调用层次,可对类作如下划分:
类 | 功能描述 | |
---|---|---|
输入处理 | Main | 读取操作与日志,向下层分发指令 |
GameManager | 游戏的管理核心,向游戏中的具体角色下发指令 | |
InputWrapper | 对 ArrayList<String> 进行的封装,简化对数字的解析 | |
Indexes | 实用类,将输入列表的下标整合起来 | |
冒险者 | Adventurer | 管理单个冒险者的属性与动作,可操作背包 |
Backpack | 维护冒险者背包中的物品,处理冒险者对于背包的交互 | |
药水瓶 | Bottle | 药水瓶的基类,被调用者使用 |
BottleRegular | 普通药水瓶,继承自 Bottle | |
BottleReinforced | 强化药水瓶,继承自 Bottle | |
BottleRecover | 百分比恢复药水瓶,继承自 Bottle | |
装备 | Equipment | 装备的基类,被调用者使用 |
EquipmentRegular | 普通装备,继承自 Equipment | |
EquipmentEpic | 百分比伤害装备,继承自 Equipment | |
EquipmentCrit | 暴击装备,继承自 Equipment | |
食物 | Food | 食物类 |
商店 | SingletonShop | 单例商店类,负责收购并记录物品,以及创造、出售物品 |
日志记录 | LoggerBase | 日志抽象类,提供存储与字符串转化的约束 |
LoggerBottles | 冒险者喝药的日志类 | |
LoggerNormalAttack | 冒险者进行普通攻击的日志类 | |
LoggerAoeAttack | 冒险者进行 AOE 攻击的日志类 |
其中,冒险者、药水瓶、装备与食物共同实现了 ICommodity
接口,提供了获取价值、获取属性、获取归属类名的方法。
商店类中,实现了 TradeLog
内部类,对通过上述接口对三类商品的收购记录进行管理。
UML 类图
二、迭代与重构过程
五次作业,四次迭代
第二次作业是对游戏中角色的基础实现,笔者同时设计了 GameManager
类进行全局的管理,以简化主类的功能。
第三次作业添加了背包的概念,背包属于冒险者,理应由冒险者管理,需要达到背包对 GameManager
隐藏的效果。同时,这一次作业也是对数据结构选择的考察,背包中采用怎样的形式来组织物品是一个很重要的话题。
第四次作业添加了对战斗日志的支持,除了考察正则表达式,也是对输入处理的一次不小的考验,同时,战斗日志层级较高,笔者考虑直接由 GameManager
进行管理。
第六次作业添加了价值体的概念,并设计接口进行统一的管理,同时,为物品添加了不同的种类,考察类的继承。无论是三种药水瓶还是三种装备,它们分别有一个共同的基类,笔者在进行开发时,利用这两个基类与接口,简化了代码的复杂度。
第七次作业是对单例模式与观察者模式的考察,以及被笔者忽视的工厂模式。涉及计算的细节较多,很考察前期设计的结构。
一次代码重构
在第四次作业通过后,处理输入部分的代码已经极为臃肿,并且为了迎合风格检查,对不同的操作做了很多混乱的拆分。在第四次作业后,我对这一部分的代码进行了重构,采用表驱动的方式,为每个操作新设方法,对后续的迭代产生了较好的帮助。
还差两次代码重构
观察上面的 UML 类图,可以发现,药水瓶、装备、食物的创建集中在两个类中:SingletonShop
与 GameManager
,并且在实现时,有大量重复的代码(比如对类别的判断),因此,这里需要的重构是使用工厂模式,用于“生产”这些物品。
同时,考虑到药水瓶、装备、食物中有大量方法的重合,应该考虑抽象出一个共同的接口,已简化其中冗余的代码。
三、JUnit
使用心得
JUnit
的合理使用可以帮助检查代码中潜在的漏洞,分支覆盖率的要求可以强制程序员再次思考代码的逻辑与含义。
在这一次的迭代作业中,笔者编写了一系列的单元测试,对单类的功能进行了较为详尽的测试,同时也包含了一些综合测试,主要针对 GameManager
类。
在实际开发中,我遇到了一定的困惑,首先,综合测试应该不属于单元测试的范畴,是我为了覆盖率和偷懒采取的一种简便策略。此外,对某些类进行测试时,为了获取运行状态,需要在类中额外添加 getter
方法,这是我不太愿意做的一件事情,也许会有更好的处理方式。
四、课程学习心得与建议
先导课程学习心得
这是我第一次很正经地使用面向对象程序设计的方法,并且从简到繁一步步迭代开发,感受到了面向对象设计思路的优点与魅力。在开发时,我可以将精力专注于当前正在做的事情,但这也给整体的谋篇布局带来了不小的考验。
希望在今后的学习中,我能够逐步参悟面向对象思想的精髓,并不断进步!
对课程的建议
- 进一步明确并教授单元测试的方法,根据项目实际调整测评标准。
- 指导书可以将教程与题目描述分成两个板块,而不是在一篇文章中前后放置,这样可能会方便阅读与查找。