OO第二次博客作业—17373247

OO第二次博客作业

零、写在前面

OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利。

(挂三次评测分数 看出得分接近等差数列 菜鸡本质暴露无遗)

一、总体设计策略

三次作业设计策略基本相同。前两次是四个类:Main、Elevator、Taker、Waiter;两个线程:main、elevator。第三次在此基础上多了一个类:Dispatcher,一个线程:dispatcher。之所以前两次没有调度器类,是因为由于只有一个电梯所以直接把调度器安在这个电梯内部了。其实算一种偷懒行为。

基本算法是若Taker队列为空,电梯从Waiter队列中取一个主请求,将其送到目的层,然后在途中实现捎带(可以进也可以出);然后送达后若Taker队列非空,则从其中选一个Taker作为新的主请求。调度器的功能是为电梯分配相应的Waiter队列。

由于架构合理,三次作业没有出现重构现象。

本来想在第三次作业的调度器类采用单例模式,但是实现上出现了一些问题,于是没有采用(后来证明并不是单例模式的问题,而是线程安全的问题,最基本的ArrayList线程不安全,自己属实智障了)

二、基于度量的分析

第一次作业

总体结构分析

第一次作业由于是首次接触多线程编程,事实上最初还是遇到了一些语法上的困难的;但是解决了基本的语法问题之后,剩下的算法问题就轻而易举了。(据说第一次100分遍地都是)

但是幸运的是自己在第一次作业便打好了合理的架构,便使得之后两次作业只需要添添补补就可以轻松实现,让自己在这个单元没有尝到重构的滋味。

扫描二维码关注公众号,回复: 5991515 查看本文章

基本架构:将Request分为Taker和Waiter两类,其中Waiter类要增加一个toTaker方法,其余基本完全对称。而在电梯内部也几乎是完全对称,基本原因就是只有Waiter才能进,只有Taker才能出。但是采用了轮询。

存疑:采用了线程安全的阻塞队列(BlockingQueue),然而相关知识并不十分完备,所以只用了一些最基本的方法(比如poll),还有很多可以深入学习。

复杂度分析


可以看出基本没有红字,这也是我所说的架构合理的一部分。Elevator类里方法过多也是没有办法的事情,因为自己觉得这个实现也非常自然了,实现可能啰嗦一些但是思路是绝对简单清晰的(而且第一次已经实现了捎带)。

第二次作业

总体结构分析

从来没有比第二次作业更轻松的一次了;因为捎带这个事情上一周就写好了,只要稍微改一改数值(而且中测1次AC)。除此之外处理了轮询问题。但是捎带的算法肯定比较low了,因为只有92分的强测成绩。

复杂度分析

可以看出基本上和第一次无甚出入。

第三次作业

总体结构分析

第三次作业终于不能再偷懒了;终于加入了调度器线程。加入调度器线程之后线程交互性大大增加,这也使得这次作业和之前两次出入巨大。但是幸运的是Elevator类基本还是那个Elevator类,也就是说电梯该怎么跑还是怎么跑,接人捎带人都还是电梯的事情,调度器只是为了分配给电梯相应的乘客而已。也算是大大减小了第三次作业的设计难度。但是优化实在是基本形同虚设,还是自己太菜了,性能分基本为零。

复杂度分析

可以看出调度器的加入导致复杂度大大增加,这也是总体结构分析中说的一部分。

SOLID原则

SOLID原则指:单一责任原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口分离原则。

单一责任原则:每个类只负责自己的事情。自己认为做的还不错,挑毛病的话可以是前两次没有调度器,或者说调度器在电梯里。但是单电梯的话,也就没必要去分配乘客,所以是完全可行的。第三次中,调度器只负责分配乘客,电梯只负责运行。

开放封闭原则:软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。三次作业可扩展性很强,事实上,不用重构而只需要修改参数就说明了这一点。而封闭可以将电梯上下行、接送乘客设计为抽象方法,但是本次作业不必要这样做。

里氏替换原则:派生的子类应该是可替换基类的。继承Thread类是唯一的继承,这和设计层面的继承是两个概念。

依赖倒置原则:高层模块不应该依赖低层模块,相反,他们应该依赖抽象类或者接口。也就是强调高层模块的抽象。本次根本不涉及什么高层模块,也就不用管。

接口分离原则:类不应该被迫依赖他们不使用的方法,也就是说一个接口应该拥有尽可能少的行为,它是精简的,也是单一的。三次作业根本没有用接口,就连Thread类也是继承,没有去实现对于Runnable的接口。

三、bug分析和互测策略

bug分析

三次作业没有出现强测的WA点,所以只谈一下编程过程中、最后一次提交前遇到的问题。

其实最主要的还是线程安全问题……这个问题贯穿于三次作业中,主要是,这个问题可能很不起眼,但是却很致命(说的就是你,一个小小的ArrayList不安全花了一个晚上的时间来debug)。而且多线程调试的问题实在是与之前的程序差别太大了,所以尝试了很多调试方法,然后到头来得到了一个真理:printf大法好!(滑稽)

互测策略

互测说实在的,根本没有针对性。屋子里也是要么一片宁静祥和,要么大家象征性地空刀几下。但是互测也不是没有它存在的意义,自己就是在互测中膜了某位巨巨的代码增进了自己对于单例模式的理解(虽然后来没有成功运用到自己的代码之中,但是自己确实深刻感受到这个是好东西啊)。

总的说来,三次作业没有强测bug,没有互测bug,不用bug修复,除了性能分不是太好看之外,属实惬意许多(想起了自己第三次求导bug修复了一天,那种滋味真的酸爽)。

四、心得体会

第一,一定要重视合理架构!尤其是在第一次作业之中。因为第一次作业是最简单的,所以在解决语法问题之后算法问题就轻而易举,那剩下的时间干啥呢,偷懒吗。。或者说,从最开始就思考问题对象最本质的特征,按特征分配类和对象。一个合理的架构会为之后的二、三次作业带来极大的便利。重构一时爽,话是这么说,但相信没人想每次都从头再来吧?

第二,一定要重视线程安全!多线程与单线程编程最大的区别、也是最大的差异就是线程数量的增加(这不是废话么),但也正是因为这是最主要的区别,所以它带来的线程安全bug也就是最大的差异,而别的bug基本都是之前的那种bug了,因此那就无需多提。

五、写在后面

三次作业虽然没有WA点,但是调度算法并没有找到一个太满意的算法。第二次也就是单电梯简单,所以直接类似SCAN算法完事,得分也还不错,估摸着进A组了;第三次轮到多电梯,依旧是SCAN算法就不大好使了,原因就是没找到一个好的分配等待队列的算法,结果就是强测性能分基本为零咯。

抛开分数不谈,通过了这三次电梯作业才真正让自己对面向对象编程思维有了较为深刻的理解,从中稍微窥得OO的本质,也算是自己的很大的一个进步了吧。

猜你喜欢

转载自www.cnblogs.com/wzh31891579/p/10759827.html