OO第二单元总结

一、设计策略

1.第一次作业

类图

第一次的的功能相对基础,但是可以预见,如果后两次作业不重构的的话,这次的调读策略将很严重的影响到后两次作业的调读策略,因此这次的策略非常重要。在经过了自己查阅资料和查看讨论区同学们的讨论后,我最终选择的是look算法构建电梯。具体实现上,我基本套用了生产者-消费者模式,除了主类中,我还设有Input,Buffer和Elevator三个类,分别对应生产者,托盘和消费者。生产者Input类不必多说,只有读取输入和把reqeust丢入Buffer两个功能,托盘Buffer我设计的功能比较少,只包括放、取requeust和读所有request几个功能,具体的策略我放在了消费者Elevator类中,Elevator自己获得自己当前的状态,通过读托盘中的request判断下一步应该接人还是反向,直到程序结束。在这样的设计中,需要保护的就只有托盘中的队列,因此只需在Buffer类的相应方法加上synchronize关键字即可。

2.第二次作业

类图

第二次的作业就要复杂一些,需要综合各个电梯的状态,给每个电梯分配适合的任务。在这次的设计上,我在上次的基础上增加了BigBuffer类(起名能力捉急),具体的流程是Input类把所有请求丢入BigBuffer类,再由BigBuffer把每个请求分配给适合的电梯对应的Buffer类,即存在Input-BigBiffer-Buffer-Elevator这个四级传递链。理想很丰满,但是当我在具体写代码时,发现BigBuffer要实现把每个请求准确的分给对应的电梯有难度,因为需要知道的信息包括每个电梯的当前人数、方向、楼层以及电梯对应Buffer的人数和各个请求,要写一个综合这么多信息的方法不仅复杂而且容易出错,于是,本着“能写对是最重要的性能分是啥”的摸鱼思想(然而最后还是有bug。。。),我简化了BigBuffer的调读策略:将请求分给(电梯内人数+Buffer区内人数)最小的那个电梯,同时,为了使每个电梯的使用时间更平均,我设计的一个阈值,即当电梯人数+缓冲区人数高于这个值时,不再给这个电梯分配请求。这样一来,需要保护的对象除了BigBuffer和Buffer两个缓冲区的对象外,就只剩下各个电梯的人数,写起来相对简单一点。

3.第三次作业

类图

第三次作业增加了电梯的类型,每个类型的电梯有自己的属性和可以停靠的楼层,与上一次相比,主要的难点时增加了“换乘”这一概念,为了解决换乘的问题,我建立了PersonRequestPro类,该类继承自PersonRequest类,增加了要换乘的电梯的类型和一个辅助的int性变量flag。这里稍微讲一下flag的原理,flag的取值有'b000----'b111八种,

首先先建立一个数组,数组的下标对应每个楼层,数组的元素反应了能到达该楼层的电梯类型,比如第0个元素对应-3层,只有A型电梯可以到达,所以元素是'b001,即1;第3个元素对应1层,ABC型电梯都可以到达,所以元素是'b111,即7。这样将每个请求的from楼层对应元素和to楼层对应元素做或运算得到该请求的flag,如果flag是0,说明该乘客必须换乘;如果flag不是0,则说明至少有一类电梯可以直达,就直接送入对应类型的电梯buffer区。我实现换乘的原理大概是这样,可以预见,性能分估计也不会很高(最后也确实如此,强测才98分多,而大佬们都99.5+。。。),还有一些细节型的问题,不一一赘述了。

二、可扩展性

1.单一责任原则(SRP)

整体大致符合单一责任原则,main类负责启动,input类负责读取输入和放请求,bigbuffer负责分配请求给下一级buffer同时保证同步,buffer类负责存放请求同时保证同步,elevator类负责取出需要的请求并打印对应输出。

2.开闭原则(OCP)

部分符合开闭原则,整个程序的三次迭代中,我整体的大框架都没有怎么修改,只是有增加,比如第二次多了BigBuffer,第三次由personrequesetpro继承了personreqyest,但是细细想来,其实每次电梯的改变也可以通过继承来实现,但我选择了修改代码,这一点并不符合开闭原则。

 

3.可替换原则(LSP)

符合可替换原则,涉及子类父类的只有PersonRequestPro,该类只是增加了两个属性,没有破坏父类的约束。

4.接口分离(ISP)

这次没有自己创建的接口,不符合接口分离。

5.依赖反转原则(DIP)

这次没有自己创建的抽象接口,不符合依赖反转原则。

 

三、程序结构分析

1.第一次作业

2.第二次作业

3.第三次作业

 

 

 

四、我的bug分析

1.第一次作业

第一次功能较为基础,没有出现bug。

2.第二次作业

第二次作业有1个bug,但是是个大bug,导致直接分入了c房中的c房.....简单来说就是bigbuffer的run方法中判断结束少break了一层。具体来讲,在bigbuffer类中有一个方法是遍历每个电梯,如果有需要分派请求的电梯则返回该电梯的编号,否则返回-1,在输入结束后,由于我少break了一层,又会执行一遍之前的方法,并直接给返回值对应的电梯分配一个请求,结果就出现了给-1号电梯分配请求的悲剧....进而导致出错,bigbuffer无法结束,整个程序也没法结束,最后满屏rtle。。。

3.第三次作业

第三次也是有1个bug,严格来说这应该是第二次作业遗留的bug,只是第二次没测出来,第三次测出来了。其实也是个低级的错误,大概是

while(methoda!=-1&...){

...

}

int x = methoda;

add(x)//给第x个电梯的buffer加一个请求

由于bigbuffer本身是在改变的,可能在while循环中判断出来不是-1,但出来后再调用就又是-1了,最终造成数组访问出错,最终加一个条件语句就解决了。

五、分析别人bug的策略

 

由于没有搭建自动评测机,这个单元其实我的hack也不十分有效。。。采用的方法也只是把写代码时想到的比较特别的数据记下来,然后在互测的时候测别人,实测命中率并不高...最终我也只在第二次的c中c房才hack到了别人,第一次和第三次只有惨淡的0/21和0/14,有时间还是该学一下自动评测的...毕竟也能用来测试自己的程序,而且写一个顶三周,还是挺值的。

六、心得体会

 

这个单元里我第一次接触了多线程,感觉推开了新世纪的大门,多线程里的随机进行、锁、竞争资源都非常有意思也很有挑战性,多线程和之前的单线程完全时两回事,需要考虑的东西也不太一样。再比如说轮询这个事,我之前写程序的时候经常会使用,这也导致了我第一次作业的第一次提交是满屏的rtle,还记得当时等了半天评测结果,最后齐刷刷的rtle的场景,这也让我意识到,可能我之前的程序无意间浪费了不少cpu的资源。

另外还有一点,和上一个单元相比,我想说不用每次重构真是太香了,每次感觉工作量都不是特别大,犹记得上一单元第三次作业要推倒重构的绝望....在这个单元,我在第一次作业中就对后两次大概会怎么增加要求有了猜测,并联想自己的程序需要怎么增加才能实现或者说根本实现不了,需要重构。最终我后面两次作业都没有重构,这一点是让我感觉自己在进步的点,当然也有可能是这一单元的重点在多线程课程组考虑的重点不是这方面的事....

总之,真切的发现自己有进步,也更真切的明白自己和年级里的大佬之间还存在非常大的差距,自己在之后的课程中还需要加倍努力呀。

 

 

posted @ 2020-04-18 20:51  ljy12138  阅读(129)  评论(0编辑  收藏  举报