309
社区成员
发帖
与我相关
我的任务
分享
第二次作业:只实现了MainClass、Adventurer、Bottle、Equipment四个类,五条指令对应的方法都在MainClass中实现。
第三次作业:本次迭代对程序架构进行大改,奠定了最终架构的基础。将MainClass中的内容拆分至InputHandler、AdventurerManager、ItemOperator类中,使MainClass仅作为程序入口;新增Item类,将Bottle、Equipment、Spell作为Item的子类,将两种药水瓶作为Bottle的子类,将四种法术作为Spell的子类;新增Usable接口,在Bottle和Spell中实现。
第五次作业:新增Armour、Magicbook、Sword、Weapon类,管理不同类型的装备,其中Magicbook和Sword是Weapon的子类;新增Fight类,实现fight相关功能,由于战斗逻辑较为复杂,所以单独开一个类;新增Factory,按要求实现工厂模式,替换addBottle、addEquipment、learnSpell方法中的相关内容。
第六次作业:本次迭代引入观察者模式,新增Observer和Subject接口,同时在Adventurer类中维护观察者(下级)的列表,以实现下级对上级的援助操作。
第七次作业:本次作业工作量不大,只新增了一个Lexer类进行词法分析,并在ItemOperator中新增loadRelation方法,维护一个栈,存储雇佣关系树中根节点到当前冒险者的节点的路径。
以往我们常使用黑盒测试的方法对程序进行测试,即给出输入样例、输出样例,不关注程序内部逻辑,只需比对程序输出结果是否和样例一致。虽然这种方法可以直接判断程序的正确性,但如果程序出现错误,就难以直接定位。而 JUnit 单元测试不同于以上方法,每一个测试单元负责测试程序的某一部分逻辑是否按照预期工作,特别是对于复杂的程序,我们将测试任务分解,就可以直观定位到哪个方法、哪项功能实现时出现了 bug,不用再大海捞针,这是 JUnit 的优势所在。
在完成开发任务的时候,我们需要思考测试的时机问题:是先开发后测试,还是边开发边测试?一开始我采取的是前者,而且我对 JUnit 不是很熟悉,还是按照传统的方法进行测试,但这样下来程序中的 bug 积少成多,然后再费尽心思 debug,最后编个 JUnit 做做样子,整个过程十分痛苦。但后来我逐渐熟悉了 JUnit 测试方法,掌握了迭代开发的规律,每增加一条指令,就同步增加对应的测试,确保每一个方法的行为都符合预期,这样才能顺利推进开发。
在具体实现时,我将所有的测试方法放在一个测试类 MainClassTest 中(当然 MainClass 本身没啥好测试的),每项测试采取读入指令的方式,先解译再执行,判断结果是否符合预期(如下)。虽然覆盖率达标,但感觉这种方法不太理想。原则上每个主要的类都要有对应的测试类,每个方法要有对应的测试方法。今后还要注意改进测试方法,注意测试分工要清晰明确。
@Test
public void testUseHpBottle() {
ArrayList<ArrayList<String>> commands = new ArrayList<>();
commands.add(new ArrayList<>(Arrays.asList("aa", "Alice")));
commands.add(new ArrayList<>(Arrays.asList("aa", "Bob")));
commands.add(new ArrayList<>(Arrays.asList("ab", "Alice", "Bot1", "HpBottle", "30")));
commands.add(new ArrayList<>(Arrays.asList("ti", "Alice", "Bot1")));
commands.add(new ArrayList<>(Arrays.asList("ar", "Alice", "Bob")));
commands.add(new ArrayList<>(Arrays.asList("use", "Alice", "Bot1", "Bob")));
processor.processCommands(commands);
Adventurer bob = manager.findAdventurer("Bob");
assertEquals(530, bob.getHitPoint());
}
@Test
public void testUseUncarriedBottle() {
ArrayList<ArrayList<String>> commands = new ArrayList<>();
commands.add(new ArrayList<>(Arrays.asList("aa", "Alice")));
commands.add(new ArrayList<>(Arrays.asList("aa", "Bob")));
commands.add(new ArrayList<>(Arrays.asList("ab", "Alice", "Bot1", "HpBottle", "50")));
commands.add(new ArrayList<>(Arrays.asList("use", "Alice", "Bot1", "Bob")));
processor.processCommands(commands);
Adventurer bob = manager.findAdventurer("Bob");
assertEquals(500, bob.getHitPoint()); // 未携带,无法使用
}
通过 OOPre 的学习,我首先了解了类、方法、实例等新概念,然后从接口、继承的运用中体会到面向对象编程的封装性、继承性、多态性,又接触了设计模式和递归下降的知识,学会了通过分解任务、复用代码来优化程序架构,实现了由面向过程到面向对象的思维转变。
OOPre 课程提供了 checkstyle 工具,在评测时检测代码风格并评分,使我意识到形成良好代码风格的重要性。规范微观层面的方法名、变量名等细节,可以使代码更加可读、更加美观,避免程序潜在错误;设定宏观层面的类长度、方法长度等限制,有助于落实单一职责原则,使程序任务划分更加清晰。
此外,OOPre 的学习还是一个锤炼心性的过程。我第四次作业因读错题,怒交 9 次才通过;第五次作业中测蒙混过关,强测竟发现了 3 个不同的 bug。Debug 是一件永恒的事,在完成复杂编程任务时,为了更好地避免 bug、修复 bug,我们需要沉下心来,冷静分析问题。
吴老师和各位助教辛苦了,感谢你们的辛勤付出!