面向对象课程第四单元作业总结

面向对象第四单元作业总结

一、第四单元架构设计

1.1 思考过程

本单元涉及到UML图的解析和查询,那么架构设计的核心便在于存储结构的设计。由于原本.mdj文件中元素的存储是使用树形结构来组织的,那么为了保证良好的可扩展性,笔者便按照.mdj文件中的Json结构以树形结构来组织UmlElement的存储。

确定总体存储结构之后,接下来的问题是在树中存储什么元素的问题。

一个简单的想法是将结点的id存储在树的结点中,然后使用一个MapidUmlElement之间建立一一对应关系。但是仔细思考之后会发现,这种存储结构会大量重复访问映射关系,可能造成大量的开销;如果想要避免这种开销,不得不在树的结点中添加另外的从UmlElement中解析出的信息,而这样又会造成信息冗余,更加麻烦的是在扩展的过程中可能会需要在树节点中添加新的成员变量和成员方法来适应新的需求。

经过上面的思想内讨论,发现从给出的结点中提取信息来填充树的结点是一件费时又费力的事情,那么一个自然的想法便是在建立树的过程中仅仅进行最小程度的解析,将所有信息都保存下来(方法是将整个类封装在一个树结点类当中)。

1.2 模型建立

建模的过程中笔者将存储结构进行了三层抽象

最底层是树结点的具体类型,比如UmlClassUmlInterface等,实际上笔者为课程组提供的开源包中的每一个Uml...类都提供了一个对应的树结点类型。当然有许多的类结构在实际查询中并不需要使用,但是为了对称性和所谓的可扩展性,况且也不影响性能,笔者还是将这些冗余的部分保留了下来。这样做还有一个好处便是能够将查询涉及到的操作尽可能分散开来,降低单个类的复杂度。

向上一层是一个类UmlNode,是树结点类型,下层所有的类都是这个类的子类。UmlNdoe类提供了最基础的对其中所封装的UmlElement的属性的查询,例如getName()getElementType()等。

在向上一层是结点类型。实际上在本次作业的情境中,这一层完全可以和树结点层合并,笔者将它单独拿出来的原因实际上是想践行一下泛型的概念。

未命名文件 (16)

当然除了这三层抽象之外,笔者还使用了一些接口来进行统一访问,比如UmlEndPointUmlLifeline的下层同时存在Message,那么便可以让这两个类同时实现一个HasMessage接口。

1.3 其他细节

两次作业的设计实际上没有什么很大的区别,所以也不必特地分开说明。

至于第二次作业中涉及到的算法,也无非是DFS和Tarjan算法,没有什么需要特别说明的。

最后一点,在包装每一个UmlElement类的时候,使用了函数式接口来组织每一个UmlNode的构造函数,简化了代码结构。

二、架构设计及OO方法理解的演进

2.1 第一单元

第一单元的作业刚刚启动的一段时间里实际上仅仅是对Java语言的熟悉过程,与面向对象程序设计的思想相差甚远。虽然看上去存在多个类的协同,实际上是将过程式编程的过程划分为几个阶段,为各个类分派不同的过程。

本单元最后一次作业是虚假的面向对象与真实的面向对象的分水岭。将所有涉及到求导的函数抽象成一个运算符类,然后将这些类划分成多目、单目和零目运算符,最后进一步抽象出一个函数类从而实现统一访问。

在这个阶段笔者对于面向对象的理解还比较细碎,仅仅时接触到了一些接口统一访问可变类与不可变类的概念。

2.2 第二单元

第二单元吸取了上一单元重构代码的教训,在第一次作业的时候便设计了沿用三次作业的架构。将用户输入,调度器和电梯分别设计成三个线程,用户输入和调度器、调度器和电梯之间分别设置一个共享对象用于数据的交互。

在这一单元接触到了SOLID原则和多线程编程。在调试多线程程序的过程中,遇到了强测也没有发现的bug,这也让笔者深刻感受到学习以及在程序中应用各种成熟的设计模式的重要性。

2.3 第三单元

使用GraphInfo类将MyRailwaySystem与具体实现逻辑完全分离,MyRailwaySystem所有对图的增删查改都通过GraphInfo提供的接口实现,增加扩展性;使用工厂方法针对不同问题创建不同的类,将寻路算法(Floyd_Warshall)单独封装,提升代码复用率;GraphInfo类和具体算法类之间共享变量,防止由于不同算法要求同样的输入时重复存储,造成空间和时间上的浪费。

为了减少时间复杂度需要将中间结果存储下来,图连通结构或边权值发生变化时只需要对特定的结果进行更新即可。在更新机制上选择check-then-update机制。

本单元重点突出了良好的设计对于程序的重要性。书写规范的JML实际上是一个设计的过程,良好的设计使得在实现程序的时候在脑海中有一个清晰的架构层次。提前规定的函数接口也能够大大避免边界上bug的出现。

2.4 第四单元

这一单元是对于本学期的学习内容的一次综合性检验。在动手编码之前首先进行细致地设计,从需求出发将模型抽象为不同的层次。在编码的过程中,面对各种设计时没有考虑到的情况,进行架构上合理的调整,或者使用一些编程上的小技巧来对程序的结构进行优化。

更多的思考写在第四部分课程收获中。

三、测试理解与实践的演进

在过去的学习过程中也接触到了测试的概念,但是并没有实际大量地将测试应用到实际中来。

在表达式求导单元中,一方面程序比较简单,另一方面没有什么测试的意识,所以仅仅在类的main方法中写了简单的测试。后来在强测中出现了bug也在意料之内。

在电梯调度单元中,由于程序的正确性与线程之间的协作相关,程序局部的正确性不一定能够保证程序整体的正确性,所以使用python做黑盒测试。在强测中也没有出现bug。

在后来的作业中,使用了Junit白盒测试与脚本黑盒测试相结合的方法,然而结果并不是每次都令人满意。

经过多次即使做了测试,结果仍然差强人意的作业,笔者深刻体会到了完成代码和完成任务之间间隔巨大的鸿沟。保证程序正确性的基础是编码之前良好的设计,良好的设计使得在编码过程中不必要多次修改架构,增加不确定性。测试本身并不能保证程序的正确性,只有完备全面的测试才能够保证程序的质量。

四、课程收获

技术层面。学习了一门新的语言 —— Java,学习了JVM、Java多线程等知识,了解了lambda表达式、stream、函数式接口等小的技术点。

意识层面。增强了规范编码风格的意识,体会到了测试的完备性之于程序正确性的重要价值。

思想层面。初步在脑海中建立了遵循设计模式进行建模的意识,遵循一些公认的规则,比如SOLID等,能够大大增加程序的可读性和可维护性。

五、课程建议

一学期走下来,笔者能够从课程网站的设计,讨论区助教和老师们的活跃度体会到OO课程组在课程进展中付出的努力。谁也不能做到十全十美,但是仍然瑕不掩瑜,在这里冒昧的提出一些小的改进建议。

  • 忌虎头蛇尾。到了学期末尾,虽然大家都面临着各个科目的考试,但是并不是没有时间来完成课程项目。即使考虑到大家时间繁忙,完全可以将完成作业的时间延长,而不是推迟发布指导书。一鼓作气,再而衰,三而竭,指导书一拖再拖消磨的是大家对这门课程的积极性。
  • 注重阅读高质量的代码。笔者认为写代码是一项要求创造性的活动,在将拥有的知识付诸实践的时候,往往由于对于知识的理解不够深入而导致实践效果差强人意。而通过阅读高质量的代码,得以一窥大佬们的思考方式,对自己带来的提升常常比闭门造车效果好得多。
  • 希望改进JML单元。在做JML单元的时候,各种上古工具几乎没有使用价值,在“尝鲜”的过程中浪费了大量的时间且没有什么实际的效果,得不偿失。

转载于:https://www.cnblogs.com/javadrinker/p/11078707.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值