OO第四单元总结
一、本单元架构设计
本单元每次作业都直接在上一次作业的基础上直接对新需求进行添加,所以下面分析第三次作业的架构设计即可。
首先是对类图的建模,新建了MyClass, MyInterface, MyOperation三个类。MyClass中包含了UmlAttribute,MyOperation,继承的MyClass和实现的MyInterface,MyInterface包含的继承的myInterface,操作。对每个类的查询都会在MyClass的对象中进行查询。
状态图和顺序图也是同样的,对状态图建模,新建了MyStateMachine和MyState两个类,MyStateMachine包含了所有的MyState和UmlTransition,MyState则包含了由当前状态直接可达的所有状态。对顺序图建模,新建了MyInteraction和MyLifeline两个类,MyInteraction包含了所有的MyLifeline和UmlMessage。
新建了Init类来进行所有模型的建构。先将所有的UML元素按照实际类型分发到该类型对应的容器中,然后开始自顶向下的构建模型。以类图为例,先遍历UmlClass所在容器,创建出对应的MyClass,然后遍历UmlAttribute所在容器,将其添加到对应的MyClass中,同样按照UmlOperation创建MyOperation,并添加到MyClass中去,还要把方法对应的参数都添加到MyOperation中去。在理解了元素之间的层次关系之后,这些工作就好做多了。
然后是对8个规则的检查,001对每个MyClass的属性和关联对端检查在Init类的初始化阶段就已经完成,直接汇总所有类的结果即可。002对循环继承的检查,我使用了搜索所有可能路径的方式,遇到环不加入路径,将该节点加入到重复节点的容器中去。但这样导致了遇到菱形图会严重超时的情况。bug修复阶段打算改成对每一个点进行DFS,如果能访问到源点,说明该点是循环继承的。003检查重复继承,对每个MyClass,如果它的父类是重复继承的,则将它直接标记为重复继承,否则,递归的向上进行检查,004检查重复实现也与之类似。005-008都在Init类的初始化工作中进行检查,如果类图元素出现名字为Null的情况则将对应的标志位置为True,同样,如果接口中出现非公共属性也将标志置True。
二、四个单元中架构设计及OO方法理解的演进
第一单元:这一单元主要是考虑层次化的设计,理解继承和多态,这也是我写java程序的开始。从功能角度,整体上分为解析字符串,求导,化简,输出这几部分,因为当时的思想还比较简单,化简和输出没有建立单独的模块。除了化简,其他的模块都比较容易完成。第三次作业中,我建立了Factor接口,包含了求导方法。Term类包含了常数因子,三角函数因子,多项式因子,表达式因子,表示这些因子的乘积关系。Expression类同样实现了Factor接口,包含了Term,表示这些项的相加关系。按照这样的方式构建一个表达式,可以清晰的看到它们的层次关系,并且可以按照固定的策略进行求导和化简。第二次作业强测95分,第三次强测100分,但在互测阶段都被找到了一些小bug。
第二单元:第一次接触了多线程,设计模式主要是生产者消费者模式。本单元第二次作业就完全采用了这一模式,电梯请求输入类作为生产者,将乘客请求发送到调度器,同时唤醒电梯,电梯作为消费者,每到达一层或者被生产者唤醒时将获取请求。每个电梯的优先级是相同的,它们去公平的竞争请求。当调度器队列为空时,电梯将退出。
第三次作业是四个单元中最复杂的一次,我改变了设计模式,不再是电梯直接去竞争请求,而是变为由调度器按照电梯的运行状态去分配请求。调度器也作为一个线程,在新的请求到来,或者电梯到达每次层时,调度器会被唤醒,扫描整个等待队列,将合适的请求分派给合适的电梯(使用了打分机制,每次分配时对所有电梯进行打分,分配给得分最小的那个,若分数太高则不进行分配),这样可以优先对乘客进行服务,一定程度上保证了乘客的满意度。对于换乘请求,采用了固定楼层换乘,因为在多个楼层换乘会增加开门的次数,大量随机的情况下,开门的次数会严重影响电梯的运行效率。这单元课程还是更注重对多线程的理解,我重点考虑的是每个线程唤醒其他线程和被其他线程唤醒的时机,在某个事件到来时进行唤醒,思考了1天之后才想清楚,着手实现就容易多了。
第三单元:这一单元基本只需要按照规格去实现代码就可以,架构上不用太多的进行考虑,但是直接使用暴力方法是容易超时的。对于计算平均值,方差等方法,在类中设置了变量来更新计算它们所用数据,而不是每次都重新计算,还有涉及最短距离和强连通的方法,建立了图后使用dijstra和tarjan算法。JML让我体会了设计与实现的分离,既方便了我们设计程序,同时也便于我们开展单元测试。
第四单元:最后一个单元是UML,架构设计则按照UML的思路进行建模,其中存在树和图,每一个属性和方法都能通过parent找到它所在的类,形成一个树,而不同类之间又可以通过关联关系来形成一个图。模型有效性的检查的002-004实际上就是对图的查询。
三、四个单元中测试理解与实践的演进
第一单元做的是比较随机的测试,使用python按照正则表达式生成的字符串进行测试,效果不太好,没有找出自己的bug,也没找到别人的。
第二单元测试不是很好进行,我只进行了一些简单的尝试,没进行全面的测试,也正因为如此,电梯第三次作业中在全部请求都是从最低层到最高层的情况下,我的电梯每次只能捎带一个人,后来发现是捎带的条件写错了,因为偷懒导致的错误又一次发生了。
第三单元使用了JUnit进行测试。前两次作业没有用Junit,第三次作业在ddl截止前一个小时打算尝试一下,结果发现有一个方法没有通过,顺着源头去找,发现是上一次作业遗留的问题。当时心情瞬间就凉了很多,好在最后在9点50分成功提交上去,差点翻车。
第四单元由于比较简单,没有太多地进行测试,基本就是肉眼debug,或者构造了几个简单地样例进行测试。
总的来说,测试真的很有必要,我从开始的盲目测试,到第三单元使用JUnit进行单元测试,更容易定位到程序的bug所在,也更容易找到bug。结合规格,更容易设计我们的测试样例,更有覆盖性的去测试。
四、课程收获
在学习这门课程之前,我没有写过Java程序,第一个收获就是我能够写Java了,与C语言相比,Java可以直接使用很多封装好的方法,而C语言还要自己去实现,如果这些作业要让我用C语言来写,那我真的会很难受。使用Java确实提高了我的工作效率。
第二个收获就是学到了一些面向对象的设计方法,让我意识到设计的重要性。前两个单元我都会仔细思考,如何去设计类,它们之间的层次关系又是如何,它们之间是怎样交互的。我感受最深的还是第二单元,先有一个整体的架构,使用生产者消费者模式或者观察者模式,然后明确输入类、调度器类、电梯类的职责,还要考虑这些线程之间的交互关系。而在思考清楚它们的关系之后,再对具体的功能加以实现,专注于一个类要实现的功能编写代码,明确每个类要完成的职责。
然后是认识了规格化设计,使用规格可以做到设计与实现的分离,在工程中,使得不同的人可以更高效的编写代码,只要规格是对的,那么按照规格实现就能够保证代码正确性。
最后,测试是必不可少的。测试能够帮助我们找到潜在的bug,充分的测试或许可以在强测时救自己一命。有几次作业没有好好测试,最后还是有一些明显的bug没有发现。不过要保证程序的良好运行,更多的是要有合理的架构,具有良好的可维护性,可扩展性,以便在应对新需求时,能够有较少的改动。
五、改进建议
首先是每次实验都没有反馈,个人感觉每次实验对作业有很好的启发性,但没有反馈又不知道自己理解的是否到位,可以给出参考答案就更好了。
然后是最后两个单元,虽然注意力转移到JML和UML的体验上,不过增加了一些有关图的查询,需要用到某些算法,可能是出于平衡难度的考虑,但是我感觉缺少了一些面向对象设计的特点。我觉得第三次作业可以给出方法的文字描述,让同学自行设计规格并实现。
关于理论课,我个人感觉有点抽象,每次在写完作业后才能理解。我觉得能否在讲课过程中,多引入一些例子,更详细深入一些。
最后就是bug修复,强测被扣太多分可能只是因为一处错误,通过bug修复能否给强测再加一些分呢。(有一次调用的方法名写错了导致了那次强测全军覆没,当然完全是我自己的问题,但是太惨了)
六、线上学习的体会
在线上少了很多与同学老师直接交流的机会,很少和别人交流架构设计,多了很多自主学习,研讨课也是极好的,学习其他优秀同学所作的分享,扩展了知识。也是因为疫情,很少出门,在家里的时间基本就是写OO,好像写OS的时间还不到OO的一半,这门课确实很硬核,现在回想起来,觉得那些作业也不是不可逾越的,付出了努力还是有回报的。
总的来说,OO确实是一门优秀的课程,除了有两次得到低分深受打击以外,整体体验还算不错,感谢老师和助教们的付出,给我们带来这门精品课程。