309
社区成员
发帖
与我相关
我的任务
分享
如图,这是我最终的架构设计。其中
MainClass调用。Bottle与Spell抽象类应用。Equipment、Weapon、Bottle、Item是抽象类,声明了相关方法,并在子类实现这些方法。Adventurer、Bottle与Equipment类,在Adventurer类中实现了Bottle与Equipment类的容器,进而实现了各种物品的加入与删除功能;加入MainClass处理输入输出。Adventurer增加了体力值相关属性;通过继承在Bottle类下实现了不同的药水瓶;添加了Spell类;为Bottle与Equipment类增加了一个顶层的Item类,便于删除物品与增加物品;在Item类中引入了isInBag的属性,并定义改变它的方法,实现背包功能;设置Usable接口,并应用于Spell与Bottle类,同时在Spell与Bottle中实现了接口定义的useThings函数,实现药水与魔咒使用功能;设置Out类专门负责输出Adventurer类中的容器部分改为了HashMap,提高运行速度;删除了Item类中isInBag属性与相关方法,创建了Bag类,创建了加入装备与药水的方法,实现了真正的背包;通过继承在Equipment类下实现了不同种类的装备;设置Factory类,负责装备、药水与魔咒下面的实体类的构造;在Adventurer中增加了战斗功能的方法;为Adventurer增加金钱属性,并在Adventurer类中实现购买装备功能。Adventurer类中设置了直接上下级的容器,形成了上下级的树,并在Adventurer与MainClass中加入了雇佣与解雇的方法。Token、Lexer、Parser、AdvNode类,实现递归向下法的语法分析。其中Token作为语法词法的最小对象,Lexer进行词法分析,Parser将Lexer产生的Token列进行分析,构造AdvNode组成的树,并用这个树来生成Adventurer树。在使用JUnit时,我觉得最重要的是考虑到尽量多的情况。为了构造出更全面的单元测试样例,我会对被测试的每个函数内部的分支进行分析,按照分支情况构造数据。我也会特别注意一些极端情况,比如0、负数与大数。
每次作业的JUnit,我都会保留上一次作业的JUnit代码作为这一次测试的一部分。这样可以进行回归测试,检测我在这次作业中的代码改动是否影响了我已有的代码(例如不小心改动了上一次作业的代码或是没有考虑到上一次代码的局限性),进而进一步保证我代码的正确性。
使用JUnit确实很有效果。在第三次作业,我就用JUnit在我的代码中发现了大量Bug。修复这些Bug不仅让我通过了弱测,还为我的进一步开发奠定了基础。
对我来说,学习OOpre课程最重要的是让我初步养成了面向对象编程的思维范式。在进入大学前,我接触过Python的编程;在大一,我又系统地学习了C语言与数据结构。可是学习完这些后,我的编程思维仍然停留在面向过程层面(虽然Python实际上有面向对象的编程范式)。那个时候,我在编程时主要思考的是程序语言的语法,是不同语句之间的衔接,是如何拆分不同的函数,但却陷入到了语法与过程的细枝末节当中,缺少一种顶层的设计。这样写出的代码不仅缺少复用、更难以维护:比如我现在已经完全无法读懂我上学期写的数据结构代码了。
在学习完OOpre之后,对于一个需求,我现在首先思考的不再是那些逻辑与算法的细节,而是代码的架构与对象之间的关系。具体来说,就是一个需求可以分为哪些功能,将哪些功能抽象并封装为类与接口,在类中如何分配任务设计职责单一的方法,如何设计类之间的静态关系(继承)与对象之间的动态关系(组合、聚合)等等。接下来,我才思考每一个方法的具体逻辑与算法细节。这使得我的代码模块性更强、耦合度更低,更容易看懂,也更容易迭代与改进。
此外,我也深刻体会到了代码重构的价值。重构是很痛苦,但有的时候,在现有代码架构无法适应新的要求时,重构是必要的。重构可以使得代码适应这些要求,还可以为进一步开发奠定基础。比如我在第五次作业重构之后,后面的作业就写的非常舒服。
希望能够更深入讲一些代码测试与调试的方法与技术,或者发一些相关的文档什么的,有时候找bug真有点困难。