OO Unit4总结 & 结课总结
OO课Unit4 UML解析应用技术回顾
BUAA.1823.邓新宇
2020/6/19
总结本单元三次作业的架构设计
本单元的架构设计主要是两方面。
一方面,原来UmlElement
的各类子类的信息过少,其组织方式是将图的顶点和边分开存储,虽然这在存储上有一定优势,但是在查询时效率极其低下,需要我们为其添加额外的属性和方法来重新构建组织方式。
- 在存储方面我选择了适配器模式,为每个不同的
UmlElement
编写了不同的适配器,而所有的适配器继承自AbstractElement
,它提供了抽象工厂模式的相关内容,提供了这些适配器的公共基类来统一放在容器中,还提供了getOrigin():UmlElement
方法来返回原对象,而getOrigin()
在每个适配器实现的时候,可以利用协变返回更为精确的UmlElement
类型。在编写这些适配器的时候,可以发现很多适配器具有相似的行为,或者他们之间本身就具有某种可替代性,比如UmlInterface
和UmlClass
,UmlState
、UmlFinalState
和UmlPseudostate
,在编写的时候就注意他们可能需要一些中间的小基类,而不直接继承自AbstractElement
。 - 在构建方面,我使用了抽象工厂模式,每一个适配器都有一个工厂方法,
AbstractElement
的静态属性实现了总的工厂,后者将通过UmlElement
的type属性,将其分发到不同适配器的工厂方法。这样做的好处是可以方便地在工厂方法中进行多种处理,比如尝试直接将图的边的信息存储到顶点中、将各个元素加入到id的查询池中。
另一方面,给出了4个Interaction接口需要我们来实现,而其中一个是另外三个接口的继承者。虽然接口方面是继承关系,但是在四个接口的实例中,我认为采用组合的方式更为合理。
总结自己在四个单元中架构设计及OO方法理解的演进
- 仅仅大量利用抽象\(\rightarrow\)大量利用继承\(\rightarrow\)减少继承的滥用;
- 边写边规划,缺东西再添加\(\rightarrow\)先整体分析,然后选择合适的设计模式来规划,最后选择合适的算法进行coding;
- 一股脑写完,再测试\(\rightarrow\)先理解好功能,将功能拆分,完成一个功能进行一部分测试;
总结自己在四个单元中测试理解与实践的演进
- 第一单元的内容,测试是比较容易的,因为一方面可以方便地生成批量样例,也可以利用python的库方便地获得正确答案,另一方面输入输出都是比较固定的,直接大量随机样例暴力黑盒测试就可以获得很好的效果。
- 而第二单元的内容,多线程的加入,导致输出是极不固定的,也无法方便地获得正确答案,但是这一单元可以从输出的内容进行分析来判断正确性。同时,这一单元是一个实时交互系统,输入不仅有内容,有顺序,还有时间。这一单元我单独设计了一个测试类,通过将Systerm.in、Systerm.out进行重定向来构造一个沙盒环境,由测试类定时投放新的数据,并将整体的输出进行保存和分析检查。
- 这一单元有了JML,并且学习了JUnit。通过JML的形式化描述,可以方便地编写针对每一个方法的测试,只要对JML的理解正确,就可以方便地分析边缘条件、方便地构造正确的测试样例。而通过JUnit,一方面可以将测试程序和主程序进行分开,而且可以利用IDE对JUnit的支持批量地进行测试,还可以通过JUnit提供的多种断言语句方便地进行多样的测试。
- 第四单元在测试方面,我主要是更进一步地进行了细化,将测试穿插在整体程序的构造之中。
经过这一学期的锻炼,我的测试策略主要分为两种,一种是针对每一个方法(尤其是复杂的方法)撰写单元测试,一种就是直接重定向输入输出进行整体的黑盒测试。单元测试可以穿插在编写主程序的时候就开始编写,完成一个功能就可以进行一步单元测试;而整体的黑盒测试甚至可以在主程序编写前进行,而且对于不同的程序,黑盒测试的主要程序可能基本都是一样的,大体流程就是从文件读取测试样例\(\rightarrow\)重定向程序的输入输出\(\rightarrow\)通过MainClass.main()
运行程序\(\rightarrow\)检查输出是否合理。需要变动的大概也就是以下几个方面:
- 如同第二单元那样,需要实时交互的,就不能简单地将测试样例文件流重定向为Systerm.in,而要通过管道来实现,运行主程序也不能直接
MainClass.main()
,而要为其创建一个线程; - 如同第四单元那样,主程序的IO并不是利用Systerm.out/in,而是在一个private的构造函数里规定的,而public的工厂方法只能创建使用一个固定的IO流来进行输入输出的AppRunner,这无法通过简单地
Systerm.setOut()/setIn()
来重定向,我选择了在单元测试中利用反射机制获取了那个private的构造函数,而提交的主程序中并没有使用。
总结自己的课程收获
其实我在学习OO这门课之前,自己认真看书《Thnking in Java》,自己也在利用OOP的思维在基物实验的仿真实验设计里进行了大部分工作。可以说我的抽象、继承、多态等等方式其实在这门课之前已经有了初步的认识了,这门课我最大的收获不在于此。这门课我主要的收获有如下几点:
- 实践的机会。这门课给了我大量实践的机会,这么多实践机会让我尝试了各种组织方式,让我感受到有些方式不可取,有些设计是画蛇添足,而有些方式很好用;
- 形式化描述与层次化设计,以前多是写到哪儿感觉不合适再重构,而现在开始注重设计,先设计,再动手coding,思路会更加清晰,后面几次作业coding的速度也比前几次快了不少;
- 测试,以前都是直接手动输入一些样例来进行测试,而通过一学期的学习,我开始渐渐习惯单元测试;
- 设计模式,结合1,经历一番bug的毒打,我渐渐意识到设计模式的诸多优点,遵循一定设计模式可以使得自己的设计程序员友好,也可以避开一些坑;
立足于自己的体会给课程提三个具体改进建议
- 建议第2单元的内容后移,一方面第三单元和第四单元的内容并不依赖于第二单元,另一方面第二单元确实有点复杂,需要对Java更多的理解,但是在很多同学没有系统学习Java的情况下,最好还是让同学们有足够的时间熟悉Java再进行多线程的编程;
- 第四单元也用JML描述,既然第3单元已经学习了JML,那么第四单元继续使用也没什么不好,也不至于现在这样出现不少歧义;
- JML的工具链,很多工具链我至今都没配置好,我希望课程组能够给一定教学和支持;
谈一谈线上学习oo课程的体会
- 代码量很大(虽说很大部分是我没事儿找事儿,为了实现作业要求之外的可拓展性,强行加入了一些作业用不上的功能),确实是一种锻炼。上了规模的代码维护起来确实很费神;
- 最大的还是收获了大量的工程方法,层次化设计方法、测试方法。我打算利用所学,重构基物实验的仿真实验设计的代码,并且为其添加形式化描述和单元测试,然后作为一个开源项目进行维护;
- 这一学期的学习,从各次作业的指导书上,我认识到文档的重要性,作业指导书,本质上是一种需求文档,每次作业的文档都写的很详细,在coding的时候我也会常常去翻看指导书来确定需求。这些指导书写的很长,也很详细,从中我也学习到了一个漂亮好用的文档应该有哪些部分;