1、最终架构设计
解释与反思
Main类:负责读入数据的处理以及基本操作框架,包含记录全部冒险者和战斗日志的容器。
Adventurer类:除Main类外最重要的一个类,包含分别记录冒险者拥有的药水瓶、装备、食物和被雇佣的冒险者的容器,并封装了对单个冒险者进行的各种操作。
Store类:单例模式,拥有静态属性及方法,主要与Adventurer类交互。在检查最终代码架构时,发现Main类承接了部分与Store类的交互工作(如Main类通过调用GetBottle等方法获取Bottle的引用后,再传递给Store类实现卖出操作),经过反思后,认为可将卖出、购买操作统一在Adventurer类中实现,降低代码复杂度。
Bottle/Equipment/Food类:在观察其它同学代码后,发现可以用Interface定义三者的共同行为。但由于认知尚浅,个人感觉Interface只是预先定义了行为规范,并不会影响方法的具体实现。或许在大型项目中灵活运用Interface,可方便项目的管理,增加代码可维护性。
RecoverBottle/ReinforceBottle/CritEquipment/EpicEquipment类:在学习子类与继承后,摒弃了最初在Bottle/Equipment/Food类中使用typ属性区分物品类别的做法,改为对父类的属性和方法进行继承。
Data类:用于对战斗日志进行封装。(同上,也可对三种战斗日志分别新建子类)
FightingAd类:用于对战斗模式中参与的冒险者进行封装,其引用被记录在相应的Data类中。(略显冗余,因为其本质就是一个ArrayList)
迭代中的架构调整
hw2:创建Main/Adventurer/Bottle/Equipment几个基础类,编写基础操作。
hw3:新增Food类,在Bottle/Equipment类中新增bring/empty等属性。
hw4:新增Data/FightingAd类,在Main类中实现字符串匹配/战斗日志保存与查找等操作。
hw4带来了本次迭代中唯一一次强测挂掉的体验。原因在于竞赛出身的笔者不忍心写暴力折磨评测机,于是在代码中加了些许性能优化。比如查询冒险者在战斗模式下作为被攻击者的有效战斗日志时,为了避免对AOE-attack型日志(dt.Type()==3)中被攻击的冒险者序列进行重复查找,于是引入了contain变量记录查找结果,仅在战斗日志所属的战斗场次发生变化时对查找结果进行更新,结果却疏忽地把更新操作写在了AOE-attack型数据的分支里,骗过了中测,败在了强测。
hw6:新增Bottle/Equipment的typ属性(后改为RecoverBottle/ReinforceBottle/CritEquipment/EpicEquipment四个子类),新增Adventurer类中QueryPriceSum/QueryMaxPrice等方法。
hw7:新增Store类,增加战斗模式结束后的援助操作。
2、使用junit的心得体会
在笔者看来,Junit将手动从控制台输入数据的传统测试方式自动化,并更加清晰地标注了测试数据的覆盖范围,初步反馈测试数据的强弱程度。同时,junit使便捷地测试程序中的某个模块成为可能,大大简化了对程序模块进行拆分测试时的复杂操作。
3、学习oopre的心得体会
在学习oopre课程前,笔者仅通过C++的vector/map等STL粗略地体会过面向对象的编程思想,也从未编写过含有复杂逻辑的大型项目。Oopre是我在面向对象编程思想上的启蒙课,课程组通过五次迭代作业,让我体会到面向对象编程思想在大规模工程中发挥的重要作用。相比于面向过程的编程思想,面向对象思想将问题中的主体和行为抽象为类和方法,将问题由类之间的交互和协作完成,有效实现了功能的解耦,增加了代码的可维护性。
令笔者较为满意的是,整个迭代开发过程中并未出现代码的大规模重构,这与笔者在hw2中就开始尽可能使用面向对象的编程思想有关,可谓面向对象代码逻辑可维护性的有力论证。
4、对oopre课程的简单建议
1、适当增加对git和junit的教学,减轻助教的答疑负担。
2、增加对HashMap等容器实现原理的讲解,虽然它是数据结构的内容。(身边有同学因为不理解HashMap进行的是无序遍历,最后一次强测挂了)