- OO第四单元总结及学期总结
- 第四单元三次作业架构设计
- 第十三次作业
- 第十四次作业
- 第十五次作业
- 四个单元中架构设计及OO方法理解的演进
- 第一单元
- 第二单元
- 第三单元
- 第四单元
- 四个单元中测试理解与实践的演进
- 第一单元
- 第二单元
- 第三单元
- 第四单元
- 小结
- 课程收获
- 改进意见
- 线上OO学习体会
- 第四单元三次作业架构设计
OO第四单元总结及学期总结
第四单元三次作业架构设计
第十三次作业
需求分析
本次作业需要实现一个UML类图分析器,根据传入的UML类图元素UmlElement建立模型、重建关系,并对询问指令作出应答。
在本次作业中有七个类,MyClass
类和MyInterface
类实现了AllClass
接口,并且采用了工厂模式,对于UmlElements
的解析在ElementFactory
中完成,MyOperation
以HashMap
存储在MyClass
类中,由该类进行管理,MyClass
和MyInterface
以HashMap
存储在MyUmlInteraction
类中,由该类进行管理,构成了一种层次化的设计。在查询时,MyUmlInteraction
类询问MyClass
类或MyInterface
,MyClass
类询问MyOperation
类。被询问对象储存在哪个类,由哪个类给出答案。
对于多数询问,都设置了缓存,以本次作业中较复杂的CLASS_IMPLEMENT_INTERFACE_LIST classname
为例,在MyClass
和MyInterface
中增加allInterfaceList
成员变量,初始时为空,在查询时,若allInterfaceList.size()==0
,则调用其实现接口和所继承类的getInterfaceList()
,在过程中,其父类、实现接口、接口的父类中的allInterfaceList
成员变量全部进行缓存,在下次调用时allInterfaceList.size()>0
,则return allInterfaceList
。
public HashMap getInterfaceList() {
if (allInterfaceList.size() > 0) {
return allInteList;
} else {
for (MyInterface myInterface : interfaceList.values()) {
allInterfaceList.putAll(myInterface.getInterfaceList());
allInterfaceList.put(myInterface.getId(), myInterface.getName());
}
if (father != null) {
allInterfaceList.putAll(father.getInterfaceList());
}
return allInterfaceList;
}
}
第十四次作业
需求分析
在上次作业的基础上,增加对UML顺序图和UML状态图的解析,建立相应的模型,对相应的查询指令做出应答。
本次作业在上次作业的基础上迭代开发,MyUmlGeneralnteraction
作为最顶层类,调用MyStateChartInteraction
、MyCollaborationInteraction
、MyClassModeInteraction
中的方法进行查询,MyClassModeInteraction
即为上次作业中的MyUmlInteraction
类。MyStateChartInteraction
管理MyStateMachine
类,调用其方法进行查询,MyFinalState
、MyPseudostate
、MyState
实现AllState
接口,便于由MyStateMachine
类进行统一管理,在询问时,被MyStateMachine
类调用。顺序图部分,MyCollaborationInteraction
管理MyInteraction
,MyInteraction
管理MyLifeline
,在查询时也是层层调用。
解析元素依然在ElementFactory
中完成,先加入UmlClass
、UmlInterface
、UmlStateMachine
、UmlInteraction
,再加入UmlOperation
、UmlAttribute
、UmlInterfaceRealization
、UmlAssociation
、UmlGeneralization
、UmlRegion
、UmlLifeline
,然后加入UmlParameter
、UmlState
、UmlMessage
,最后加入UmlTransition
。
对本次较为复杂的查询指令SUBSEQUENT_STATE_COUNT statemachine_name statename
采用了广度优先遍历,并进行缓存。
第十五次作业
需求分析
在前两次作业的基础上,对模型进行有效性检查,在发现有违背所给出的八条规则的模型时抛出异常。
本次作业在前两次作业的基础上仅增加了一个类MyStandardPreCheck
,由最顶层的MyUmlGeneralnteraction
管理调用。MyStandardPreCheck
调用MyStateChartInteraction
、MyClassModeInteraction
的方法获取检查结果。对于查询指令没有改动。
对于R005、R006直接在MyClassModeInteraction
每次调用下层类准备add前进行判断。在MyClass
中进行深度优先遍历,同时判断该类是否违背R002和R004,在MyInterface
中进行深度优先遍历,同时判断该接口是否违背R002和R003,发现违背R002时即可停止遍历,对于R001,在MyClass
中,addAttribute()
、addAssociation()
时进行判断。R007、R008在MyStateMachine
中每次addTransition()
时判断。
四个单元中架构设计及OO方法理解的演进
第一单元
第一单元的作业为多项式求导,第一次作业完全是面向过程的方式,导致许多方法复杂度都很高,在第二次作业时进行了重构,设计了抽象类Term
,SinTerm
类、CosTerm
类、VariableTerm
类都继承了Term
,并且将大正则表达式修改为层次化的表达式。第三次作业继续重构,五种因子VarFactor
,ConstFactor
,ExpFactor
,CosFactor
,SinFactor
继承了抽象类Factor
,并重写了父类的求导方法getDiff()
、输出方法printFactor()
以及equals()
方法。整体层次上有三层Expression
类(即表达式),Term
类(即项),Factor
类(即因子),层层调用。在求导的时候,增加了AddDiff
和MulDiff
两个类,在两个类中有各自的求导规则,继承了抽象类Diff
,也采取了工厂模式。
在第一单元的作业中,算是从完全到面向过程,到开始试着使用继承、接口等,有了一些结构的设计,并且掌握了工厂模式。同时,通过本单元的作业了解了一个良好的结构的重要性,如果初始的结构混乱,之后必然是次次重构,优化也会难以下手。
第二单元
在第二单元中学习了多线程,第五次作业中采用生产者-消费者模型,调度算法采取了Look算法,但将许多调度的设计放在了run()
方法中,导致复杂度较高,也不利于扩展,第六、七次作业中采取Work Thread模式加Look算法。因为从本单元第一次作业开始即确定了设计模式和调度算法,所以在之后的几次作业对于之前代码的改动不多。对于第七次作业中加的换乘要求,也是若Request
可直达则不换乘,若需要换乘则在一开始便决定好换乘楼层,修改Request
的outFloor
为换乘楼层,新增一个Request
加入Controller
新增的用于存储新产生Request
的 ArrayList
,采用固定的换乘方案。
第二单元困难的地方在于多线程,需要考虑线程安全的问题。在保证线程安全的前提下,为了应对check-then-act,部分锁加在了代码内部,而没能很好地设计一个线程安全的共享对象解决线程安全问题,导致许多方法都不够简洁。本单元的整体结构基于多线程设计模式,但局部一些结构仅仅保证了正确性和性能,可读性和可扩展性都不强,从本单元开始思考如何兼顾正确性、性能和架构的优雅。
第三单元
第三单元是对于JML的学习,读JML规格写代码,但是如果照搬JML规格,会导致TLE,因此需要思考使用的容器、代码的算法以及动态维护。容器主要采用ArrayList
和HashMap
结合,ArrayList
用于遍历,HashMap
用于查询。关于算法,在第九次作业中,isCircle()
采用dfs的算法,第十次作业中,isCircle()
的实现改为了并查集,用HashMap
存储pre和rank,对于geConflictSum()
、getAgeMean()
、getAgeVar()
等方法都采用了缓存机制,即在MyGroup
类中addPerson()
的时候就改变这些函数相应的成员变量。在第十一次作业中,由于isCircle()
的实现采用并查集,queryBlockSum()
只需设置成员变量blockSum
,在addPerson()
时加一,在addRelation()
时,若find()
函数结果相同则减一,queryMinPath()
采用堆优化的dijkstra
算法,利用优先队列实现堆,存储PersonEdge
。queryStrongLinked()
采用了Tarjian
算法,记录了每个连通分量,判断连通分量中是否包含起始点和终止点。
第三单元对JML的学习理解保证了代码的正确性,但本单元作业困难的地方在于算法。JML规格对于代码的正确性有很大的帮助,同时让结构更好、方法复杂度更低,可以专注于每个部分的性能的提升。经过三次作业后,感受到设计好整个架构以及每个方法的作用之后再开始实现代码,能很大程度上提高效率,减少重构的可能性,更能专注于每个部分的实现。
第四单元
第四单元是对UML类图、UML顺序图和UML状态图的解析,具体实现见上述。在经过前三单元的学习之后,本单元从第十三次作业开始根据UML的特性设计了层次化的结构,后两次作业均在该基础上迭代开发,选取了适当的容器、算法,并对部分查询进行了缓存,算是一种比较面向对象的思考方式。同时,在熟悉UML图之后,感觉这也是一个描述代码功能结构的很好的工具,在实现代码之前画好UML图可以提前分析设计的结构是否合理,是否符合要求。
四个单元中测试理解与实践的演进
第一单元
第一单元需要构造多项式,然后判断自己的求导结果。测试时,主要根据指导书,构造各种特殊的测试样例,如结果为0或1,或系数为1或-1,或指数为1或-1或0,多个sin的嵌套等,然后逐步增加样例的复杂度。这是方法虽然可以检查一些特殊情况,但比较低效,需要花较多时间,覆盖度较差,并且在样例复杂的时候也很难计算结果。
第二单元
第二单元需要构造上电梯请求,然后判断电梯的运行情况是否正确。测试时,新建一个类,自动生成随机请求并输入程序。对于运行情况的判断主要包括上下人的数量,以及人上下电梯的楼层正确与否,电梯是否有特殊情况,如无人上下但是开门,是否一层层运行,有无在禁止开门的楼层开门。并且可以通过改变生成请求的时间间隔和楼层的范围检查结果,加长请求间隔时间可检查电梯能否正常await()
、signnal()
,增大和减小楼层范围可判断算法的正确性,以及优化的方向。
第三单元
第三单元需要生成各种指令,然后判断反馈结果是否正确。在第三单元,用python进行自动化测试,随机生成测试样例,并用networkx
包建相应的图,可调用nx.dijkstra_path_length(G,source=id1,target=id2)
、nx.single_source_shortest_path(G, i1)
等方法测试几个算法较复杂的方法,对于其他函数的测试,则直接对应JML规格在python程序中无脑循环,检查结果是否一致,同时用time.perf_counter()
计算程序运行时间,使程序运行时间大致上不出现太大的偏差。
第四单元
第四单元需要构造UML模型,然后判断查询指令的结果是否正确。本单元比较难随机生成样例,因此手动画各种特殊情况的UML图,如各种同名、继承实现的各种圈儿,然后导出,观察程序运行结果,判断是否正确。然后在输入查询指令时,测试各种异常,如查询不存在的类等。
小结
从第一单元手动构造测试样例,到第二单元在程序中随机生成数据进行测试,再到第三单元用python进行自动化测试、同时判断程序运行时间,测试的覆盖性越来越强,程序的正确性也有了很大的保障。
课程收获
这一学期的OO学习让我受益匪浅。从刚开始只专注于程序的正确性,一拿到题就开始吭哧吭哧写、写完吭哧吭哧改,到开始在写代码之前思考代码的结构、结构的可扩展性和算法的性能,在有一个大致的脉络之后再开始写代码的工作。面向对象的思维对代码的结构也有很大帮助,思维方式的改变提高了效率,减少了重构次数,也有利于代码的正确性、可扩展性和性能。
在结构方面,了解并学习了许多设计模式的应用,如:工厂模式、生产者-消费者模式、Work Thread模式等,并学会通过封装继承、实现接口等方法建立一个较好的架构,降低代码的复杂度、耦合度,更利于代码的扩展。
在算法方面,开始重视容器的选择,并且学习到了许多新的算法,在保证正确性的基础上,提高代码的性能。一些情况下,缓存机制对于代码性能的提高也有很大帮助。OO第三单元也让我花费很多时间查阅博客查找资料,了解学习了一些比较难的算法,培养了自己查找资料进行自学的能力。
在测试方面,从第一单元手动构造测试样例,到第二单元在程序中随机生成数据进行测试,再到第三单元用python进行自动化测试,对我来说是一个较大的进步,意识到了测试的重要性,自动化测试对代码的正确性、优化的方向也有很大帮助,在代码交上去之后终于不是刚开始那种听天由命的感觉。
同时,在不知不觉间也提高了自己编程的能力、debug的能力,能更加耐心专注,也提高了查找资料自学的能力。并且了解了一些如JML规格、UML图之类的工具,以及多线程对未来的学习和工作都有很大作用。总体来说,过程有多艰难,收获就有多少。
改进意见
- 部分OO理论课上的内容较为抽象,比较难理解,关于一些抽象的思想,希望可以更多地结合作业讲解,因为对于作业是最了解的,结合作业解释更能体会到那些思想的优势,以及如何应用。同时,理论课上对于写作业实际需要学习的内容涉及也较少。
- 诚然,OO研讨课上有许多非常宝贵的经验分享,十分实用,让人豁然开朗,但还是有一些内容,感觉对于当下来说作用不是很大,过于高端。
- OO第三单元是对于JML规格的学习,但是感觉这单元对于算法的要求较高,主要时间都花在了算法的学习上,而不是对于JML规格的理解或者写出JML规格的能力。
线上OO学习体会
线上的学习对我来说,其实并没有很大影响,甚至有许多好处。网课可以在之后反复观看,让我能更好地理解课上的内容。而且,对于一个不怎么参与讨论的人来说,可以看到大家在群里的讨论,给我的帮助也很大。并且,即使在家编程,OO也是一门让人很容易投入的课程,在家学习的效率比其他学科高很多。总体来说,OO的线上学习除了让老师助教们花费了更多的精力,实验课有提交不上的风险之外,对我个人来说,是利大于弊的。