面向对象第四单元总结暨课程总结
作者:互联网
面向对象第四单元博客暨课程总结
目录
一、本单元作业的架构设计
本单元通过设计一个UML分析器,让我们清晰地了解了UML相关的构成要素和结构。
第一次作业
本次作业,需要完成的任务为实现一个UML类图分析器
UmlInteraction
,学习目标为UML入门级的理解、UML类图的构成要素及其解析方法。
本次作业主要任务是对UML中的类图进行解析,然后根据输入的指令查询相关的信息,并通过此次作业让我们对UML类图的相关结构有一个清晰的认识。
本次作业乃至本单元作业的重点在于选择什么容器保存相关的信息,以及如何处理名字可能会重复的问题。我的思路是在容器中主要保存各个元素的 ID,而不是name或者别的信息,最后使用一个从 ID 查询元素具体内容的容器来查询具体的信息。
我的程序主要在构造函数中进行UML的解析。在UML中与类图相关的元素主要有九种,并分为三层:首先是类和接口UmlClass、UmlInterface,然后是类中的元素UmlAttribute、UmlOperation、UmlParameter,最后类之间关系UmlInterfaceRealization、UmlGeneralization、UmlAssociation、UmlAssociationEnd。
在构造器中,由于我们的数据输入是没有顺序的,因此我们需要多次遍历对应的参数来进行初始化。
-
类和接口UmlClass、UmlInterface
- 这是UML类图中的核心元素,其他的所有元素或多或少都与这两个有牵连,比如UmlAttribute的parent是class,Generalization的source和target是class。因此我们在解析其他元素之前,首先要解析所有的类的接口。
-
类中的元素UmlAttribute、UmlOperation
- 解析完类之后,就可以解析类中的元素,本次作业中主要是属性和方法。
-
UmlParameter
-
由于参数的parent是operation,因此我们需要在解析完所有方法后,对参数进行解析,并根据参数的类型对方法进行分类
-
如果参数类型为IN,则将该方法的ID加入
classID2operationParam.get(classID)
这个HashSet容器中,由于是集合,则多次添加同一个方法不会产生副作用 -
同理,如果参数类型为RETURN,则将该方法加入
classID2operationReturn.get(classID)
这个容器
-
-
类之间关系UmlInterfaceRealization、UmlGeneralization、UmlAssociation
- 了解了类接口、以及其中的属性和方法,接下来我们可以来看看宏观角度上类与类之间的关系
- 接口和实现都通过一个 HashMap 保存 Source 和 Target 的 classID 即可
- 关联关系则又要分两步,我在第五点中对此进行说明
-
UmlAssociationEnd
- UML中与类相关的有 UmlAssociation 和 UmlAssociationEnd 两个元素,个人理解是 UmlAssociationEnd 是对关联关系中一端的类的包装,在类的基础上加入了数量等信息,包装成为 UmlAssociation 中的 end。
- 因此我在第四步中就保存了 end2end 的信息,然后在遍历 UmlAssociationEnd 的过程中查询 end 与 end 之间的关联关系,并将 UmlAssociationEnd 拆解出 classID 加入 classID2reference 这个容器进行保存,并在加入 classID2reference 的过程中同时更新 classID2referenceCount。
以下是我在第一次作业中使用到的容器:
// 保存所有 UmlElement 元素的HashMap,方便后续根据元素ID进行查询
private HashMap<String, UmlElement> umlElements = new HashMap<>();
// 保存了所有 UmlClass 和 UmlInterface 元素的 ID 的 HashSet
private HashSet<String> umlClassId = new HashSet<>();
private HashSet<String> umlInterfaceId = new HashSet<>();
// 由于我们的查询函数提供的是 className,因此我使用了一个通过 name 查询 ID 的 HashMap
private HashMap<String, ArrayList<String>> className2classId = new HashMap<>();
/**
*以下是关于 Arrtibute 的容器,通过 classID 查询其中的属性的名字和ID
*由于我们在getInformationNotHidden()函数中需要查询是否违反隐藏的要求
*因此我将可见性不是Visibility.PRIVATE的Attribute也保存了起来,也可以通过 classID 进行查询
*/
private HashMap<String, HashMap<String, String>> classID2attribute = new HashMap<>();
private HashMap<String, HashSet<String>> classID2exposedAttribute = new HashMap<>();
/**
* 接下来是关于 Operation的容器
* 前三个容器通过 classID 查询对应的类中的不同类型的操作
* 第四个容器则保存了通过 classID 查询的类中的操作的具体信息,包括了 name 和 ID
*/
private HashMap<String, HashSet<String>> classID2operationTotal = new HashMap<>();
private HashMap<String, HashSet<String>> classID2operationParam = new HashMap<>();
private HashMap<String, HashSet<String>> classID2operationReturn = new HashMap<>();
private HashMap<String, HashMap<String, HashSet<String>>> classID2operation = new HashMap<>();
// 保存了实现和集成关系,由于UML中实现和继承都不唯一,因此使用 HashMap 和 ArrayList 进行保存
private HashMap<String, ArrayList<String>> realizationSource2Target = new HashMap<>();
private HashMap<String, ArrayList<String>> generalizationSource2Target = new HashMap<>();
/** 最后是关于类之间关联关系
* 前者可以通过 classID 查询关联类的数量
* 后者可以通过 classID 查询具体的关联类
* 注意!!!这里不能用 HashSet.size() 来替代对应的数量
* 因为自关联的情况下,HashSet 中只增加了一个元素,而计数器要增加两个
*/
private HashMap<String, Integer> classID2referenceCount = new HashMap<>();
private HashMap<String, HashSet<String>> classID2reference = new HashMap<>();
第二次作业
本次作业,在上次作业基础上,扩展解析器,使得能够支持对UML顺序图和UML状态图的解析。
与第一次作业类似,我们通过加入对UML顺序图和UML状态图的解析,学习掌握相关的元素构成。
对于 Uml 顺序图,首先解析 UmlInteraction 元素,然后解析 UmlLifeline 和 UmlMessage 元素。
对于 Uml 状态图,首先解析 UmlStateMachine 元素,然后解析三个状态 UmlState、UmlPseudoState、UmlFinalState 以及状态的转移 UmlTransition。
大致思路与第一次作业类似,通过容器保存这些元素的相关关系,然后在方法中进行查询即可。
Uml 顺序图使用的容器如下:
private HashMap<String, ArrayList<String>> interactionName2Id = new HashMap<>();
private HashMap<String, HashMap<String, ArrayList<String>>> umlLifeline = new HashMap<>();
private HashMap<String, Integer> umlLifelineCount = new HashMap<>();
private HashMap<String, Integer> umlMessageCount = new HashMap<>();
private HashMap<String, Integer> umlIncomingMessageCount = new HashMap<>();
Uml 状态图使用的容器如下:
private HashMap<String, ArrayList<String>> stateMachineName2Id = new HashMap<>();
private HashMap<String, HashMap<String, ArrayList<String>>> umlState = new HashMap<>();
private HashMap<String, Integer> umlStateCount = new HashMap<>();
private HashMap<String, Integer> umlTransitionCount = new HashMap<>();
private HashMap<String, HashSet<String>> umlTransition = new HashMap<>();
第三次作业
本次作业,在上次作业基础上,扩展解析器,使得能够支持对UML顺序图和UML状态图的解析,并对模型进行有效性检查。
第三次作业主要是在前两次作业的基础上对模型进行有效性检查。
这次作业难度比较大的应该是 checkForUml002、checkForUml003、checkForUml004 这三个方法。
checkForUml002
这个函数的主要任务是对循环继承的情况进行检查,我对类和接口进行遍历,然后从每个类和接口开始,进行bfs深度优先搜索,若搜索到了跟出发点相同的类或者接口,则将这个出发的类或者接口加入专门的容器,最后进行判断和输出。
checkForUml003
这个函数主要是要求任何一个类或接口不能重复继承另外一个类或接口,考虑类之间的继承关系、接口之间的继承关系,包括直接继承或间接继承。
checkForUml004
这个函数主要是要求任何一个类不能重复实现同一个接口,考虑类之间的继承关系、接口之间的继承关系,以及类对接口的实现关系,包括直接继承或间接继承。
我们可以发现checkForUml003
和checkForUml004
在内容上是有一定重叠的。对这两个函数的设计思路差不多:前者只考虑继承关系;后者若为类则考虑继承和实现,若为接口则只考虑继承关系。然后通过bfs广度优先搜索相关的类和接口,若产生重复,则将类名加入专属的容器,最后进行判断和输出。
本单元的作业我主要是通过四个类分别进行不同类型的解析和有效性检查。
二、架构设计及OO方法理解的演进
这个学期架构设计主要体现在前两个单元。
第一单元第三次作业的架构设计如下
第二单元第三次作业架构如下:
第一种架构
第二种架构
在第一单元中,从第一次作业的类面向过程的代码到第三次作业利用了工厂模式以及不同因子类对统一接口的实现,虽然架构设计上肯定还是有诸多问题,但是也算是在架构设计上往前迈了一步。
在第二单元中,从最基本的生产者消费者模型衍生而来的第一种结构,到对调度器进行拆分,结合 SOLID 原则对程序进行改进,让整个程序的架构设计更加科学。
三、测试理解与实践的演进
在前两个单元中,我主要通过手动测试和自动测试进行测试。前者是对一些常见出错以及一些边界情况进行检测,比如第一单元的空字符串和 WF、第二单元的能否正确关闭电梯进程及线程安全等情况。除了手动测试之外,自动测试无疑是效率更高的测试方法。利用Python的自动化正则表达式生成器,可以进行大规模的测试。虽然自动化测试在覆盖度上并不能做到很广,测试数据的针对性也不是很高。但是在自动化测试的大规模数据的测试下,程序的正确性还是有一定保证的。
在后两个单元,由于JML和UML对每个方法的具体操作了都有了具体的定义,所以从一定程度上来说,更重要的是保证各个方法独立的功能实现的正确性,所以我在这两个单元是用的比较多的是利用 JUnit 进行单元测试。
四、课程收获
Pre
面向对象的学习,准确地说应该是从寒假开始的。
从最初的 Git 和 Java 到现在的 JML、UML,我们的经过这一个学期的学习确实学到了不少。
首先是 Java 的基本语法,这是我们这门课的基础,也是现在一门非常有用的编程语言。
第一单元
除了语言本身,我们通过 Java 的学习,接触到了面向对象的编程思想,这是与我们在之前的学习 C 语言的过程中体会不到的。在第一单元的学习后,对于面向对象的继承和多态等思想有了进一步的了解。第一单元的表达式中各种因子,或有不同的属性,或有不同的求导方法,或有不同的转化成字符串的方式,通过继承父类和实现接口,以及重写相关方法,在设计程序的过程中我们自然而然地增进了对相关设计方法理解。
第二单元
第二单元的核心内容是多线程并发设计,通过实验课和课外学习了不少多线程设计模式。这是非常有意思的一个单元,也是以前从未有过的编程体验。
第三单元
第三单元主要关于JML,虽然JML现在不是很主流的编程方法, 相关的工具链也不是很完善。但是通过这一单元的学习,我们了解了设计与实现分离的编程思想,更有利于未来的合作开发。在这一单元,由于每个类的每个方法的要求都是已经给出的,所以我们可以很好地利用JUnit进行单元测试,这也是这一单元学习的收获之一。
第四单元
最后一个单元的核心内容是UML。UML 类图通过图形化的方式简洁明了地表现出了各个类内部构成和各个类、接口之间关系,UML 顺序图则能体现各个类的生存周期,UML 状态图则体现了各个状态之间的转换。这一单元通过设计 UML 解析器,让我们了解了 UML 相关的要素构成。
总结
回顾面向对象课程,我觉得整体而言每个单元各有侧重,也都达到了相应的学习目标。Pre的Java语法基础,Unit1的继承和多态等面向对象的思想,Unit2的多线程,Unit3的JML,Unit4的UML——每个单元,四周的学习,都非常充实而有收获。
五、三个具体的改进建议
- 首先,我个人认为第一单元第三次作业的难度是本学期最大的一次,而第三单元相对而言是比较简单的一个单元,不知道能不能稍微降低一些第一单元第三次作业的难度或者进行一定的顺序调整,比如将JML调整至第一单元,通过JML实现方法也比较贴近于我们之前的面向过程的学习经历,个人认为更加适合刚刚接触 Java 和 面向对象的初学者。
- 其次是关于强测,在互测中同一个 bug 可以通过合并修复尽量的减少扣分,但是同一个 bug 造成大量重复扣分在强测中也是存在的啊,不过在强测中出现这个同问带来的问题则大得多,不知道课程组对这个问题有没有一定的解决方法。这个问题在整个学期的学习过程中都一直存在。
- 最后我想提一点关于实验课的建议。每个单元的第一次作业对于初学者而言往往一开始都是很难下手的,就像在第二单元的第一次作业前,在理论课上学习了线程安全的几种处理方法,但是到实际的编程过程中,我们往往都还是不太了解具体的使用方法。这里,我要为面向对象的实验课点赞,每次实验课的内容都非常贴合对应的单元的学习内容,对于我们有非常大的提示作用。所以,我的建议是,能否将实验课的时间调整至理论课和作业开始之间,这样,在学习完理论课之后,通过实验课了解相关内容的具体使用方法,再在自己的程序中进行使用,这样我们的完成作业乃至整个学习过程都会更加流畅。
六、线上学习OO课程的体会
线上学习OO课程,会更加考验学习的自主积极性,在没有了线下的当面接触后,在微信群、讨论区等网上进行交流则显得更加重要。
在此,我要特别感谢在这一学期的学习过程中给予过我帮助的同学们。每次遇到难以找到问题的bug时,讨论区和微信群是寻找问题的不二场所。在同学们互帮互助的过程中,我们可以感受到浓厚的学习氛围以及同学们热心帮助他人的品质。
最后,我还要特别感谢这一学期的课程组和助教们。因为这个学期的特殊情况,不管是老师还是助教都遇到了前所未有的状况。对于我们而言,无非是理论课改成腾讯课堂了,上机实验变成在自己电脑上了,但是对于助教或者老师们来说,无疑是增加了巨大的负担。所以,非常感谢课程组的所有老师和助教,在这个特殊的学期,给我们带来了体验良好、收获颇丰的面向对象课程。
也祝未来的面向对象课程组越来越好!
标签:HashMap,作业,总结暨,private,面向对象,课程,new,UML,单元 来源: https://www.cnblogs.com/tongtao0305/p/13130900.html