BUAA_OO_第二单元作业总结

程序设计策略

第一次作业

第一次作业实现的是FAFS式傻瓜调度电梯,由于是第一次接触多线程,难度不是很大。在我的设计里,我借鉴了生产者消费者模式,设计了一个输入线程和一个电梯线程,控制器的设计使用了单例模式,维护了一个请求队列,作为托盘使用。在运行时,输入线程负责向请求队列提供请求,电梯线程负责拿取请求,并运行到指定楼层。这次作业的缺点是,我设计的控制器较为鸡肋,其实质只是一个存储调度指令的队列,我将具体的运行逻辑全写在了电梯里面。由于电梯是傻瓜式调度,这样的设计在正确性上没有出现问题,但是导致了扩展性很差,使得我在做第二次作业时不得不重写了电梯运行的逻辑和控制器的绝大部分内容。

第二次作业

这一次作业要求实现一个可稍带的ALS电梯。由于上述第一次设计的缺陷,我重构了控制器和电梯逻辑。调度策略方面采用了近似look的策略。线程个数保持不变,输入线程保持不变,电梯线程进行了较大的改动,我将所有的运行逻辑全部剥离,电梯只负责进行上人、下人、开门、关门、沿运动方向移动一层的原子操作,而控制器负责维护一个请求队列,负责所有的请求处理。即电梯每次上行或者下行一层都要向控制器发送自身状态,控制器获取状态后,遍历整个请求队列,选出同方向捎带的进入请求和到达请求给予电梯,同时控制器修改队列里的请求状态,将到站的请求删除。电梯获取请求后根据请求内容决定是否执行上人、下人、开门、关门、沿运动方向移动一层,到了下一层再获取新的请求。
为了实现以上逻辑,我电梯里新增了几个电梯状态参数:最远到达楼层、电梯运行方向、空闲位。每次电梯运送同方向的人时修改最远到达楼层。如果到了一次运行的最远到达楼层且没有同方向的可稍带请求,那么就将空闲位置位。控制器发现有空闲电梯时,就会使电梯向目前的请求队列的第一个请求移动。若当前请求队列为空,则使电梯静止空等。
这一次作业我认为设计的保证了正确性,实现了不同模块之间的独立性。性能上有待改进的是空闲时,可以让电梯运行,而非空等。

第三次作业

这一次作业要求多部多线程智能(SS)电梯的实现。有了第二次作业的架构设计,这一次作业的主要任务就是进行了限定人数、楼层、运行速度等新增要求的扩展,还有多线程运行时的线程安全问题。为了满足人数、楼层的要求,我又在电梯类里新增了电梯的楼层、当前人员序列,传给控制器获取请求时也增加了这两个成员。为了应对请求需要拆分成多部电梯的情况,我将人员请求设计成了一个类,每个请求在读入时,按照周转楼层最少的原则进行拆分,每个人员内部建立一个请求队列,人员再组成一个大的请求队列。控制器逻辑加入了楼层判断、人满判断,其余同上一次大致相同。同时在设计时还注意了从请求队列里遍历时,只能判断每个人员内部队列的第一个请求,这是保证人员必须完成第一个请求才能换乘电梯,防止出现时序问题。
这一次作业可优化的地方有不少,例如把根据电梯实际运行情况实时切分请求、根据电梯负载分配新请求等等。

程序度量分析

第一次作业

类图

BUAA_OO_第二单元作业总结

类复杂度

BUAA_OO_第二单元作业总结

方法复杂度

BUAA_OO_第二单元作业总结

时序图

BUAA_OO_第二单元作业总结

SOLID设计原则评价:

SOLID原则分析

  • SRP:几乎所有的调度逻辑全部丢给了电梯类,控制器几乎无作用。
  • OCP:由于电梯类包含了太多的运行逻辑,可扩展性较差,分配逻辑扩展起来很困难。
  • LSP:没有使用继承
  • ISP:未实现接口类
  • DIP:由于采用生产者消费者模式,输入和输出模块互不依赖。

第二次作业

类图

BUAA_OO_第二单元作业总结

类复杂度

BUAA_OO_第二单元作业总结

方法复杂度

BUAA_OO_第二单元作业总结

时序图

BUAA_OO_第二单元作业总结

BUAA_OO_第二单元作业总结

SOLID设计原则评价:

SOLID原则分析

  • SRP:调度器仅负责调度,电梯仅运行进行运行的原子操作,职责划分明确。
  • OCP:由于将所有运行控制部分从电梯里抽离,所以新增功能时可以轻松地扩展属性、增添调度逻辑
  • LSP:没有使用继承
  • ISP:未实现接口类
  • DIP:输入和输出模块互不依赖,但控制器调度需要获取电梯属性,依赖电梯的具体状态。

第三次作业

类图

BUAA_OO_第二单元作业总结

类复杂度

BUAA_OO_第二单元作业总结

方法复杂度

BUAA_OO_第二单元作业总结

时序图

BUAA_OO_第二单元作业总结

BUAA_OO_第二单元作业总结

SOLID设计原则评价:

SOLID原则分析

  • SRP:调度器仅负责调度,各个电梯仅运行运行的原子操作,职责划分明确,互不影响。
  • OCP:由于电梯参数在构造方法时传入,所以支持新增电梯或者属性的扩展。
  • LSP:没有使用继承
  • ISP:未实现接口类
  • DIP:输入和输出模块互不依赖,各个电梯互相不知道彼此存在,调度器通过一个消息类抽象与电梯通信。

bug相关

个人bug

在这个单元作业中,由于前两次的调度策略较简单,也没有较大的线程安全问题,我前两次作业都没有出现正确性问题。但第三次作业中,我在强测时出现了线程安全问题。举例说明我的问题:处理请求999-FROM-3-TO-2时,先拆分成999-FROM-3-TO-1以及999-FROM-1-TO-2。由于存在同步问题,可能导致在1楼换乘时,先输出IN-999-1-B再输出OUT-999-1-C的错误。这说明问题在于输出和修改人员状态的不同步。在原来的设计中,我实现的方法是在调度器修改人员状态(在哪部电梯上),同时将进电梯和出电梯的人员以列表形式传给电梯,由电梯进行输出In和out信息。这就可能导致在一个线程对一个人员状态进行修改后还未来得及输出,另一个线程就进行了下一次操作。
修改措施是增加了修改状态的判断,控制器在修改人员状态时,会判断人员是否在电梯内。若人员已不在电梯内,则证明已经输出了out,可以修改状态;否则不修改。

hack相关

第一次作业较简单,同组都是满分,没有正确性问题。第二次作业开始,除了随机生成数据以外,我hack别人时采用的策略主要来自于个人写程序时出现的问题,有以下几点:1.一层重复开关门问题,这类问题往往是判断是否转向运行时容易出现的,因此构造1-2-1这样的反复转向数据。2.开关门的间隔上人问题,这类问题主要出现在可能设计出了开关门的0.4秒不能上人的问题,针对这一点可以构造【0.0】n条请求,【0.4】n条请求这类数据,若没有在边界时间带人,很容易就会超时。但由于取消了T_max限制 ,hack他人时,基本也没出现正确性问题,倒是随机生成的简单至[0.0]1-FROM-1-TO--1这样的数据hack到了人。
第三次作业则是hack的重灾区,不幸中的万幸是我没有被hack出来强测的错误,这可能是由于相关样例不好构造的缘故。由于这次作业完成的时间紧张,我主要时随机生成数据,生成结果后再交给检查程序检查。在查看他人代码时,我发现电梯调度策略多种多样,问题也出现的各种各样,理清一个人的思路已实属不易,找出程序运行的逻辑更是难,所以我只着重查看了两个方面,一个是调度队列的同步问题,另一个是TimableOutput线程不安全问题。

这一单元与第一单元的测试策略相比,着重点从边界数据转移到了线程安全问题上,构造一些针对线程安全的数据(例如请求拆分、队列加锁、输出安全等)更容易得分。

总结

这一单元的重点是多线程相关问题,包括线程安全、线程同步设计等。重中之重就是线程安全的确保,在这一单元自己写出的bug无一不是线程安全导致的时序同步问题。相比第一单元,算法部分减少了,但多线程并发的问题更加棘手。另外的难点是调试部分,一行行print输出确实很考验人的耐心。电梯控制逻辑嵌套也很多,把所有情况分析全面很困难。总之,这一单元充满了挑战,也让我收获不少。希望在之后的面向对象课程学习里,我能继续努力,考虑更加全面,保证程序的正确性。

上一篇:BUAA_OO第三单元规格化


下一篇:BUAA-OO-第三单元作业-JML之图论