OOPre课程作业总结:属于我自己的博德之门3
属于我自己的博德之门3
一、冒险者作业最终架构设计
1. 核心实体类
Adventurer(冒险者)
系统的核心实体类,是整个系统的中心。
主要属性:
- 基础属性:
id、hitPoint、baseatk、basedef、mana、money - 物品管理:
Map<String, Bottle> bottles、Map<String, Equipment> equipments、Map<String, Spell> spells - 携带物品:
LinkedHashSet<String> bottleBackpack(最多10个,FIFO)、carriedWeapon、carriedArmour - 雇佣关系:
Set<String> employees、String employer
核心方法:
- 物品管理:
addBottle()、addEquipment()、addSpell()、removeItem()、takeItem() - 属性计算:
getAtk()(基础攻击+武器)、getDef()(基础防御+护甲) - 状态查询:
isDead()、isCarrying() - 关系管理:
hireEmployee()、isBoss()(递归查找上级)、isAlly()(判断盟友) - 援助相关:
getAllSubordinates()(递归获取所有下属)
Equipment(装备)
装备基类,采用三层继承结构。
继承体系:
Equipment
├── Weapon(武器)
│ ├── Sword(剑)
│ └── Magicbook(魔法书)
└── Armour(护甲)
属性:id、ce(战斗力)
设计考虑:Weapon作为中间层便于统一管理武器,与Armour区分开来。
Bottle(药水)
药水基类,四种子类对应不同效果。
子类:
HpBottle:恢复生命值AtkBottle:永久增加基础攻击力DefBottle:永久增加基础防御力ManaBottle:恢复魔法值
属性:id、effect
特点:使用后自动从背包移除。
Spell(法术)
法术基类,两种子类实现不同功能。
子类:
HealSpell:治疗法术,为盟友恢复生命值AttackSpell:攻击法术,对敌人造成伤害
属性:id、manaCost(魔法消耗)、power(威力)
使用规则:
- 需要消耗魔法值
- 治疗法术只能对盟友使用
- 攻击法术不能攻击上级
2. 工具类
Factory(工厂类)
集中管理所有对象的创建,实现工厂设计模式。
方法:
createBottle(String type, String id, int effect):创建药水对象createSpell(String type, String id, int cost, int power):创建法术对象createEquipment(String type, String id, int ce):创建装备对象
设计目的:
- 将对象创建逻辑从业务代码中分离
- 便于统一管理和扩展新类型
- 符合开闭原则
Lexer(词法分析器)
用于解析输入命令,将字符串分解为token序列。
核心功能:
- 去除所有空白字符
- 识别标识符(字母、数字、下划线)
- 识别特殊符号(
(、)、,)
接口:
peek():查看当前tokennext():移动到下一个token
Parser(语法分析器)
使用递归下降法解析复杂的冒险者雇佣关系。
解析文法:
冒险者 → 标识符 [被雇佣者]
被雇佣者 → '(' 冒险者序列 ')'
冒险者序列 → 冒险者 {',' 冒险者}
示例:A(B(C,D),E(F)) 表示 A雇佣B和E,B雇佣C和D,E雇佣F
设计亮点:采用编译原理中的递归下降算法,优雅处理嵌套结构。
3. 主程序类
Main
程序入口,负责读取输入、初始化数据结构,并调用相关方法处理命令。
核心职责:
- 维护全局冒险者Map:
Map<String, Adventurer> adventurers - 读取和解析命令
- 分发到对应的处理方法
- 协调战斗、援助等复杂逻辑
主要方法:
addAdventurer():添加冒险者addBottle()、addEquipment()、learnSpell():添加物品useItem():使用物品或法术fight():处理战斗addRelation()、removeRelation()、loadRelation():管理雇佣关系checkAndTriggerHelp():检查并触发援助
二、迭代中的架构调整及考虑
1. 第二次作业(初始架构)
实现内容
- 实现冒险者类
Adventurer - 实现药水瓶类
Bottle - 实现装备类
Equipment - 利用容器管理所有冒险者,并管理每一个冒险者所拥有的药水瓶和装备
核心设计
数据结构选择:
// 全局管理所有冒险者
private static Map<String, Adventurer> adventurers = new HashMap<>();
// 冒险者内部管理物品
public class Adventurer {
private Map<String, Bottle> bottles;
private Map<String, Equipment> equipments;
}
为什么使用Map?
- O(1)时间复杂度查找
- 键值对结构直观,便于通过ID快速访问
- 支持高效的增删改查操作
基础命令实现:
aa advId:添加冒险者ab advId bottleId type effect:添加药水ae advId equipmentId type ce:添加装备
架构特点:
- 简单直接,面向过程的思维
- 所有逻辑集中在Main类
- 为后续扩展预留了空间
2. 第三次作业(引入继承)
实现内容
- 实现法术类
Spell - 对冒险者添加属性(HP、ATK、DEF、Mana)
- 对Bottle类、Spell类添加子类
继承体系建立
Bottle子类化:
class HpBottle extends Bottle { }
class AtkBottle extends Bottle { }
class DefBottle extends Bottle { }
class ManaBottle extends Bottle { }
Spell类设计:
public class Spell {
private String id;
private int manaCost;
private int power;
}
class HealSpell extends Spell { }
class AttackSpell extends Spell { }
Adventurer属性扩展:
private int hitPoint = 500;
private int baseatk = 1;
private int basedef = 0;
private int mana = 10;
private Map<String, Spell> spells;
使用物品功能:
use advId usableId targetId:使用药水或法术- 药水使用后自动移除
- 法术消耗魔法值
架构改进:
- 引入继承体系,建立类层次结构
- 通过子类区分不同类型,代码更清晰
- Adventurer成为真正的核心实体
3. 第五次作业(装备体系与战斗系统)
实现内容
- 为装备添加子类
- 引入战斗功能
- 实现金钱系统
装备继承体系
Equipment(基类)
├── Weapon(武器,中间层)
│ ├── Sword(剑)
│ └── Magicbook(魔法书)
└── Armour(护甲)
设计考虑:
- Weapon作为中间层统一管理所有武器
- 冒险者可携带一件武器和一件护甲
- 通过
getAtk()和getDef()计算总战斗力
携带装备机制:
private Weapon carriedWeapon;
private Armour carriedArmour;
public int getAtk() {
if (carriedWeapon == null) {
return baseatk;
} else {
return carriedWeapon.getCe() + baseatk;
}
}
战斗系统实现:
命令格式:fight advId k targetId1 targetId2 ... targetIdk
战斗逻辑:
- 魔法书:消耗
sqrt(ce)点魔法值,伤害=攻击力(无视防御) - 普通武器:伤害=攻击力-最大防御力(需攻击力>防御力)
- 击杀获得目标的总价值(金钱+物品价值之和)
金钱系统:
private int money = 50; // 初始金钱
// bi命令:购买物品
private static void buyItem(String advId, String itemId, String type) {
int cost = getCost(type);
if (adventurer.getMoney() >= cost) {
adventurer.setMoney(adventurer.getMoney() - cost);
// 创建并添加物品
}
}
架构改进:
- 装备体系完善,三层继承清晰
- 战斗系统引入多种机制
- 金钱与战斗、物品形成闭环
4. 第六次作业(雇佣关系与援助系统)
实现内容
- 实现雇佣关系
- 实现操作后援助功能
雇佣关系数据结构
private Set<String> employees; // 雇员集合
private String employer; // 雇主
为什么用Set?
- 避免重复雇佣
- 快速查找和删除
- 不需要保持顺序
关系查询算法:
判断是否为上级(递归向上):
public boolean isBoss(String targetId, Map<String, Adventurer> allAdventurers) {
if (this.employer == null) return false;
if (this.employer.equals(targetId)) return true;
Adventurer boss = allAdventurers.get(this.employer);
if (boss != null && !boss.isDead()) {
return boss.isBoss(targetId, allAdventurers); // 递归
}
return false;
}
判断是否为盟友:
public boolean isAlly(String targetId, Map<String, Adventurer> allAdventurers) {
if (this.id.equals(targetId)) return true; // 自己
if (isBoss(targetId, allAdventurers)) return true; // 上级
return isSubordinate(targetId, allAdventurers); // 下属
}
关系检查:
- 不能攻击上级
- 治疗法术和药水只能对盟友使用
- 死亡时自动清除所有雇佣关系
援助系统实现:
触发条件:生命值降至原来的一半以下
援助逻辑:
private static void checkAndTriggerHelp(Adventurer target, int oldHp) {
int newHp = target.getHitPoint();
if (newHp > 0 && newHp <= oldHp / 2) {
// 获取所有下属(递归)
List<Adventurer> subordinates = target.getAllSubordinates(adventurers);
int helpCount = 0;
for (Adventurer helper : subordinates) {
if (tryHelp(helper, target)) {
helpCount++;
}
}
if (helpCount > 0) {
System.out.println(target.getId() + " is helped by " + helpCount +
" adventurer(s), now Hp is " + target.getHitPoint());
}
}
}
选择最优法术:
- 优先选择power最大的治疗法术
- power相同时选择manaCost最小的
- 贪心算法
架构改进:
- 图结构思维:雇佣关系本质是有向图
- 递归算法优雅处理多层级关系
- 自动化援助机制
5. 第七次作业(最终架构)
实现内容
利用递归下降法导入复杂冒险者关系
命令格式
lr A(B(C,D),E(F))
含义:
- A雇佣了B和E
- B雇佣了C和D
- E雇佣了F
Lexer + Parser设计
词法分析(Lexer):
将输入字符串分解为token序列:
输入:A(B(C,D),E(F))
Token序列:A ( B ( C , D ) , E ( F ) )
实现要点:
- 去除所有空白字符
- 识别标识符和特殊符号
- 提供
peek()和next()接口
语法分析(Parser):
文法定义:
冒险者 → 标识符 [被雇佣者]
被雇佣者 → '(' 冒险者序列 ')'
冒险者序列 → 冒险者 {',' 冒险者}
递归下降实现:
private void parseAdventurer() {
String adventurerId = lexer.peek();
lexer.next();
if (lexer.peek() != null && lexer.peek().equals("(")) {
parseEmployees(adventurerId); // 递归处理雇员
}
}
private void parseEmployees(String employerId) {
if (!lexer.peek().equals("(")) {
throw new RuntimeException("Expected '('");
}
lexer.next();
parseAdventurerList(employerId); // 递归解析雇员列表
if (!lexer.peek().equals(")")) {
throw new RuntimeException("Expected ')'");
}
lexer.next();
}
架构完善:
- 编译原理应用:Lexer + Parser的经典组合
- 递归下降优雅处理嵌套结构
- 职责分离:词法分析和语法分析独立
三、JUnit单元测试心得体会
可以很好用来测试代码,每次迭代后运行全部测试,确保新功能没有影响旧功能,还可以快速反馈,修改代码后立即知道是否破坏了现有功能,不过一开始写起来确实遇到了不少困难,毕竟大一学C的时候并不需要写这些额外的东西,在文档和ai的帮助下,写着写着就会了,不过覆盖率有时候还是不尽如人意,我会在OO中更精进一下的。
四、学习OOPre的心得体会
在知道OOPre的迭代作业是编一个类似于rpg的程序,我就非常感兴趣,因为从小到大挺喜欢打游戏,mc,tr,LOL,二游,博德之门3,艾尔登法环等等大小游戏,还不少做将来进游戏大厂编游戏的梦,所以看到作业是做一个“游戏”,非常的感兴趣,但是毕竟从前没有面向对象编程的经历,所以第二次作业很懵逼,因为一下子难度飙升,第一次只是debug,但第二次就直接是一个独立的程序了,而且需要的内容还不少,确实困难很大,毕竟之前都是面向过程编程,不知道该怎么办,从哪里起手,遂问了问s7h学长,说是要先想好都设置哪些类,每个类起什么作用,怎么交互,再写代码,有点似懂非懂,但好歹是有了个大方向,做的过程中也在问起了编程中遇到的具体问题,助教们都非常非常耐心!在这十分感谢所有的助教!随着药水,法术,战斗,雇佣,支援的引入,真的真的很有成就感,仿佛自己做了一个自己的八方旅人,做了一个自己的博德之门3,做出了一个真正的东西,真的很喜欢这样的作业,不过可能是自己能力问题?感觉一次迭代内容好多啊,一周的时间真的好仓促,希望之后能迭代速度慢一点。
五、总结与展望
建议:
1:迭代速度能放缓一些嘛?感觉进度好赶啊(也有可能是自己菜菜的
致谢
感谢OOPre课程组的老师和助教,通过精心设计的迭代作业,让我们在实践中真正理解了面向对象的精髓!做出了属于自己的博德之门3!
文章首发于CSDN
如有问题或建议,欢迎在评论区交流讨论!
如果这篇文章对你有帮助,请点赞支持 👍
相关推荐
2023Fall-BUAA-OOPre-hw-8-summary
2023Fall-BUAA-OOPre-hw-8-summary
北京航空航天大学2024年秋季学期面向对象设计与构造先导课程实践项目_基于Java语言的冒险游戏设计与实现_面向对象编程思想实践与教学演示_包含类设计继承多态接口实现设计模式.zip
北京航空航天大学2024年秋季学期面向对象设计与构造先导课程实践项目_基于Java语言的冒险游戏设计与实现_面向对象编程思想实践与教学演示_包含类设计继承多态接口实现设计模式.zip
高校技术转移办公室人员如何高效开展技术需求挖掘工作?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
科技中介服务机构如何提升服务的专业性与用户粘性?.docx
科技中介服务机构如何提升服务的专业性与用户粘性?
高校技术转移办公室人员在组织科研成果展示时,如何提升推介材料的专业性与吸引力?.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。


