270
社区成员
发帖
与我相关
我的任务
分享
在这个阶段中,相关逻辑以及不同类之间的调用还较为简单。只需要抽象出 Spell、Bottle 等父类进行管理,具体行为在子类中执行,最后的架构就会比较清晰。
此外,随着指令增多,将对指令的分解过程分离到单独的类 CommandHandler 里。最顶层的 Main 类只暴露出指令字符串的传入,然后由 CommandHandler 传入 AdventurerManager 类里进行具体操作。
值得注意的是,在 hw3 中我们需要增加的一个最重要的要求就是在执行具体的指令前,必须检查相关的冒险者的存活情况。如果在每个操作前都用 if (a.getAlive) 来判断的话,势必出现大量的重复代码,也不利于日后修改。因此我们在 Adventurer 类里增加 executeIfAlive() 方法要求所有操作必须在冒险者存活的情况下进行,将运行逻辑和判断逻辑进行分离,具体代码如下:
public boolean executeIfAlive(Runnable action) {
if (this.death) {
System.out.println(this.id + " is dead!");
return false;
}
action.run();
return true;
}
这个阶段增加了最核心的战斗系统以及其配套的金钱系统,并细化了背包系统、增加了工厂模式。
由于战斗系统常常涉及多个冒险者而且逻辑较为复杂,简单粗暴地将所有的执行都交由 AdventurerManager 类处理显然是困难的。因此我们分离出 BattleSolver 类来完成战斗的具体行为。
而随着对所携带物品要求的增加,直接用多个 HashMap 在 Adventurer 类里管理显然明显降低了代码的可读性和可维护性。因此我们增加 Backpack 类对所携带物品和其增加和去除的逻辑进行统一管理,并在 Adventurer 类中实例化 Backpack 类使得每一个冒险者都拥有这样一个符合要求的背包。
此外,随着指令数量的进一步增加,为减少单一方法的复杂性和长度,在 CommandHandler 类中对每一个指令都增加一个单独的 execute 方法将数据分发到不同的类和方法中进行操作。而原来的方法只判断指令的类型并跳转到对应的 execute 方法中。
public void execute(String line) {
String[] parts = line.split("\\s+");
String command = parts[0];
switch (command) {
case "aa":
executeAddAdventurer(parts);
break;
// ... other cases ...
default:
System.err.println(command + " not recognized");
break;
}
}
private void executeAddAdventurer(String[] parts) {
String advId = parts[1];
manager.addAdventurer(advId);
}
// ... other executes ...
在这个阶段,最后补充了雇佣系统,以及在其之上的援助功能和要求操作前基于雇佣关系进行判断。
同上所述,我们势必要增加 RelationManager 和 HelpObserver 类对相关的数据和逻辑进行管理和操作。在这个时候,我们发现在工程中,有超过一处以上的地方需要调用如 RelationManager 和 AdventurerManager 类的方法和数据,因为这个类里存储数据,我们无法在每次需要调用时重新实例化。而在不同的方法中进行传递又过于复杂和臃肿,因此在这阶段引入了单例模式来提供全局的访问。
// 在被访问类中
private static Class instance = new Class();
public static Class getInstance () {
return instance;
}
// 在需访问的类中
private static class = Class.getInstance();
class.method();
此外,我们发现对于存活情况和雇佣关系的检查位于所有操作之前,因此我们将这两个检查统一提前到 CommandHandler 里进行,便于后续的维护。
同时,随着所需类的增加,为了方便在开发中更容易找到我们需要的具体某个类,因此我们将相关较强的类在一个包内进行存储,具体分包如下:
src
├── core
└── model
├── bottle
├── equipment
└── spell
JUnit 对我来说最大的帮助是明确系统的预期行为。尤其是在处理战斗、死亡事件、关系更新等复杂交互时:
@Before 方法构造测试环境,让每个测试互不影响通过 JUnit 测试,我们不再只能根据输出结果来判断运行的情况。此外,在过去我们常在过程中插入输出来判断当前的状态,而这种方法仍需要自己进行肉眼上的对照,在测试结束后还需要一个个注释掉或删除。而现在我们可以用更完善和独立的 JUnit 测试保证复杂逻辑的安全性,也能敦促我们在开发过程中尽量注意到不同的分支和细节。
在 OOPre 前,一般我们写的基本都是流程式代码。课程的迭代设计让我开始将认知从“如何写功能”转向“如何划分对象的责任和关系”。此外,在对类的分离和提取抽象父类的过程中,我理解了在 Java 中对类的划分不再是为了区分流程而将代码分段,而是为了对应和表达实际中的角色和其行为。
在这次作业里,我开始对“怎么写架构”有了一定的认知和理解。尝试将单例、观察者模式等利用(或者说套用)在自己的代码里,能明显感觉到程序变得更清爽、更好维护。尤其是把类似解析和执行逻辑这种不同的功能进行分离之后,整个工程的结构显得更加清晰和通顺,不容易写到一半就忘记和原有代码的关联和接续关系。所以通过这次的课程能让我们认识到,写代码不只是把功能堆上去,合理地拆分和封装才是真的在“搭建”一个系统。