在本次作业中,我们最重要的任务就是根据各个UML元素的ParentId
以及Target
和Source
建立起元素间的关联关系,在根据元素间的关联关系去逐层调用方法,最后完成我们想要的查询。我在Implementation
内部建立了ID
与元素一一对应的HashMap
,这样我们就可以根据独一无二的ID
来访问任何一个元素了。之后再根据ParentId
、Target
、Source
等信息建立起元素的关联关系,最后写出特定方法进行查询即可。
类图中的关系如下图所示,箭头起点指向终点表示起点的类有终点的类的HashMap
作为其自身的属性,发现这正好与我们输入的ParentId
指向关系是相反的,通过建立起这样的逐层关系,我们可以有效地进行查找,比如查找一个类中全部方法的有关信息,我每可以在类中调用方法的查询方法,再有方法调用参数的查询方法,通过类之间的逐层协作方式完成查询,逻辑十分清晰。注 :Association
部分表示的是类只有他对面的AssociationEnd
即可。
协作图元素的关系如图所示,需要注意的是,为了便于查找,不光Message
的Parent
需要有Message
属性,LifeLine
和EndPoint
也需要有Message
属性。而添加Message时,也是先加入到Interacation
中,再有Interacation
发送到LifeLine
和EndPoint
中。
这一部分架构的难点就再状态的转移上,我们可以将三种状态都继承父类SuperState
,SuperState
中有加入下几个状态和查询下几个状态的方法,这样可以避免把Transition
存入状态中,使其仅为Region
中的属性,加入Transition
时,先将其存入Region
中,Region
再将对应的状态转移赋给对应的类。这样就完成了我们全部UML元素的逐层添加与逐层查询。
在第一单元中,我还没有形成面向对象的思维方法,设计出的类大部分的目的是将整个表达式拆开来,而不是每个类作为一个小表达式进行递归解析。整体的架构思维是以第一次作业为基准,作为自己能解析的嵌套括号层数最小的表达式,然后寻找一对内部包含可解析的嵌套括号层数最大的括号,调用第一次作业的方法进行解析,这样就将这对括号内部的括号全都消除了。对于自定义函数类型,我采用的是替换的方法,而替换是一种面向过程的方法,在实现中会产生各种奇怪的bug,这也让我第一次对一个问题用面向过程来解决还是面向对象来解决产生思考,使我开始更加自发的去学习并使用面向对象的方法,同时反复的替换会带来化简上的困难,远不如递归解析后每个因子内部包含相应的信息再来化简那样方便。但无奈由于时间有限,还是没能将设计转为递归解析的方法。
在第二单元中,我开始有意识的去想,该有哪些对象,这些对象的又该有什么方法,通过这种思考的方式,再结合当时实验课的代码,我得出了初步的设计,输入线程-等待队列-调度器-5部电梯等待队列-电梯的初步设计。之所以要对乘客进行频繁的取出与放入,是为了保证每个调度器需要考虑的判断条件都足够小,因为在理解了生产者-消费者的模型之后,代码部分不是很容易出错,容易出错的部分是判断放到哪个电梯里面的部分。在第二次作业中,为了沿用第一次作业的设计,采用了超长的食物链模型来保证将人员分配到每个电梯独有的等待队列中,虽然写起来有些麻烦,不过我认为线程安全性很高,因为全局只有一个生产者-消费者模型。而在第三次作业中,我感受到了面向对象的魅力,上课老师曾经讲过,一个对象的属性,外面看到的和自己看到的是可以不一样的,自己看到的永远只有一开始设定的出发地和目的地的楼座与楼层,而电梯看到的可以根据当前乘客所在位置来动态变化的出发地和目的地的楼座与楼层,比如在横向运输的过程中,我们可以让电梯看到的楼层是相同的,仅有楼座不同,这样就延续了上一次的设计。在这三次迭代中,我对多线程与面向对象的理解得到了进一步加深。
刚开始做时我觉得第三单元我觉得面向对象的思维方法用的不多,但后来做了第四单元后我就知道其面向对象的思维体现在哪里了。因为设计大部分有JML语言给好了,我们只要实现对应的接口即可,所以自己构造的类大多是专门用来实现图论中几个算法的类,没有什么特别的设计。第三单元给我的感觉大多是算法的优化,如果不能用一种算法的最快类型,那么大概率会导致超时。第二个给我的感觉是JML语言写起来困难,但很方便写代码的人,最开始读JML时可能会有一些困难,但之后读起来就非常容易了,并且JML语言是十分严谨的,不会出现模棱两可的情况,但JML写起来是比较困难的,在第三单元博客作业让我们自己对开放性的问题撰写JML语言,该设计哪些变量,一个方法有哪些情况,哪些变量改变,最后产生什么结果,这些都是读起来容易写起来困难的。
架构设计在上面已经讲过了,下面讲一下对面向思维的理解。第三单元与第四单元突出的特点是类与类之间的协作,在写第四单元时,我发现我开始下意识的将MyImplementation
里面的方法分解为几个类之间逐层协作的方法进行解决,在之前我一定会全部写在MyImplementation
的方法中的。这也让我感受到,第三单元主要突出了People
、Message
这两个底层类与Group
(中层类)、Network
(顶层/最大类)进行协作,Network
中的一个方法永远不是自己完成的,而是要借助其他类进行协作来完成的。所以最后我体会到,对于一个问题,要用面向对象的方法去解决,要思考有哪些类、类有哪些方法,类之间如何协作,类之间有没有共性(方法上或者属性上),类之间互相应该知道对方的什么信息等等问题,在这些方面想明白之后,问题也就迎刃而解了。
测试单元时我oo旅途中非常遗憾的一个单元,碍于时间原因,我没能在每次作业中投入大量的精力去测试。
在第一单元中,我开始采用分模块的方法进行测试,由于我在第一单元采用的思路主要是替换,而检查程序的每个替换模块是否正确是非常重要的,如果每个替换模块给出的结果都是正确的,那么将他们组装在一起得到的结果也大概率正确,所以完成作业的过程中,我每完成一个模块,都会单独对他们进行一次测试,保证模块正确后再继续。但由于最终的时间较为紧迫,加上对指导书阅读的不够深刻,我很难完成一个程序去递归构造数据,只能手动构造几个数据来进行测试,不过通过这次测试,我理解了测试的首要目的是保征数据的全面性,保证每一种可能都测试到,而一个方面测许多次是在前面已经能保证的基础上再进行的。而保证数据的全面性,首要的任务就是读取指导书。
在第二单元中,我认为程序随机生成的数据测试强度是不够大的,远不如人工构造的数据具有测试的针对性,但正确性的评判需要借助程序,因此我编写了程序来判断我的电梯输出的数据是否正确,大概的过程是根据输入让评判程序知道有多少人,他们在哪出发要到哪,再检测电梯的输出,判断乘客是否全部到达,再检测电梯的输出是否符合逻辑,比如时间戳等。同时我在测试中手工构造的最多的数据就是在一个时刻投入大量的乘客,我认为这是非常具有代表性的数据,既可以测出电梯的正确性与性能,又可以测出线程的安全性。还有一部分手工构造的数据是专门用来测试功能的,比如LOOK算法的功能是否正确,环形电梯是否会一直跑停不下来。分模块的方法在这里仍然重要,因为我第二单元主要采用超长的食物链模型,每加入一个调度器,我都要去判断这个调度器是否能正确的将电梯或者乘客加入相应的等待队列中。
第三单元的测试比较遗憾的一点是由于时间不足,我没有学习Junit来进行单元测试,我主要采用的是随机构造数据轰炸加对拍来进行的测试,因为题中给出的指令可以进行暴力堆叠,如果一次测试数据包含50万条随机指令的话,那么评测机跑一天的数据覆盖范围是很可观的。除此之外,还要手动构造一些数据,比如加完人后连续使用最容易超时的指令,来避免超时,两者结合可以有效地应对这一单元的作业。这一单元给我的理解是,针对不同的问题,要有不同的测试方法,第一单元最好递归生成数据、第二单元手动构造比随机生成有效,并要辅以程序检查正确性、第三单元随机生成巨量数据不失为一种好方法。
到了第四单元,前两次作业的明显更适合程序生成的数据来进行测试,第三次作业更适合手画UML图来进行测试,因为程序生成不好控制出错的方式,再得到数据后,再跟其他同学进行对拍即可完成,经过四单元的作业,我对测试的理解由最初的手工随机构造数据,到之后的手工覆盖构造数据,再到程序覆盖构造数据加上手工针对构造数据,评测方法由之前的肉眼评测,到程序判断对错和对拍,实践方法由手工转为程序,但遗憾的是没能使用Junit,等待之后有空闲再补上吧,总的来讲我对程序的测试方法与理解得到了提高。
我认为我通过OO课程收获最大的是锻炼了是增加了我的毅力,使我懂得了坚持的意义,最开始第一单元的作业十分的困难,无论从设计还是编写上都另我感到十分痛苦,直接熬到了截至提交那天的中午才得以完成,不过结果还好,其实中途我也产生想要放弃退课的想法,不过最后都坚持下来了,虽然第2、3次作业完成的都不是很理想,不过我都相信,只要坚持、不放弃、一点一点阅读指导书,构思设计、再一点一点实现设计,即使作业再难,也都是可以完成的。正是遵循着这样的信念,后面三个单元我都取得了满意的成绩。所以我认为OO课程给我最大收获是锻炼了我的心性,通过一次次阅读指导书使我内心变得仔细并且不浮躁,通过一次次的设计与重构使我内心变得坚韧不拔,再通过一次次的实现与成功测试使我变得自信。总之,十分感谢OO课程给我的磨练。
再上过OO课程之后,对一个问题的分析与思考有了更广阔的角度,比如把大象放进冰箱,以前认为开门,放大象,关门是一种方法,现在我认为,人、大象、冰箱是三个类,冰箱有开门关门两个方法,大象有移动的方法,而人可以调用他们的方法来完成这一任务,完成类之间的协作,这让我体会到以后有问题悬而未解时可以通过面向对象的角度去思考。
OO课程是第一门让我能写如此多行数代码的课程,看着行数不断的上涨,增加的不仅是我的满足感,更是我写代码的能力。从刚开始实现一个设计卡的码不出来,到现在可以比较容易的实现自己的设计,代码能力的上涨是让我能看的见的。与此同时,我的算法能力也有了提升,从性能分到卡超时,无一不要求我们将采用的算法优化到最好,而我也是第一次进行算法优化,以前的数据结构课也只是学了最基础的算法,开了眼界的同时也让我的算法能力得到提高。
同学间相互帮助、探讨架构、比较算法、互相测试,这也是我认为OO课非常有魅力的地方,一个人的力量总是单薄的,研讨课上大家交流架构,分享好用的工具,比较各种算法,分享测试方法与工具,再到课下同学互相对拍等等,每一次作业都不是一个人在战斗。而这些互帮互助的过程也让我收获了许多友谊。
第一单元的难度我认为可以稍稍降低一些,因为刚接触这门课时第一单元作业的难度确实很大,如果把第一单元的难度加到第二、三、四单元,我认为更加适合我们的学习。整体上我的感觉是1>2>4>3,如果能改成2>3,1>4是否会好一些呢?
第三单元对算法优化的要求是很高的,虽然我在这一单元算法优化的过程中体会到了快乐与收获,但算法可能不是OO课想要教给我们的主要内容,但第三单元的难点却集中于此,或许可以有一次作业是专门来写JML,但这样或许不好评测?
感觉平常上的理论课与我们的作业关系貌似没有那么密切,反而是实验课的一些代码对作业有很强的意义,但或许理论课与作业关系密切之后会造成作业难度的急剧下降(因为会讲很多作业相关内容)?