zoukankan      html  css  js  c++  java
  • OO第二单元作业总结

    一、设计策略

    第五次作业

     本次作业中,除了Main主线程之外还有两个线程,分别为InputHandler和Elevator。Main主线程用来创造InputHandler、Elevator和RequestQueue,并启动其他两个线程。设计上采用生产者消费者模型,RequeQueue作为托盘,InputHandler不断读取输入将请求放入托盘,Elevator采用先来先服务的原则从托盘中取出请求执行。当输入线程关闭并且所有请求均被执行,Elevator线程停止。

    第六次作业

     本次作业中,Main是主线程,启动InputHandlerx线程输入请求,启动Dispatcher线程调度电梯。

    变化一是将电梯线程分解为dispatcher类和elevator类,elevator设置了一系列的方法,比如open(),close(),inPerson(),outPerson()等控制电梯运动,调度器dispatcher调用这些方法来实现电梯调度。将调度与变化分离,是为了适应以后更复杂的调度要求,可以再增添调度器和电梯实现扩展。

    变化二是为每个请求都开了一个线程来进行调度,调度线程结束的条件该请求可以作为主请求或者可以被某一电梯捎带,否则则之后再询问调度器是否可以被执行。这样,调度器中有一个请求队列,这个队列中有一个主请求,其余请求可被它捎带。在一次电梯执行时,该请求队列中的所有请求将被执行,队列为空电梯空闲,则可再选择一个请求作为主请求,进行下一次执行。

    变化二有一个巨大的问题,由于多线程执行的不确定性,假如有多个请求均可被捎带时,并不是所有请求都会一次性进入了dispatcher的请求队列,电梯会往复执行多次,才能将其执行完。就算调节请求分配的询问时间,也无法保证所有可以一次捎带的请求都被捎带,所以许多检测点出现了问题。由于对多线程执行理解不深入,导致设计时自以为可行,结果出现了问题。

    可能的重构方法是任然采用生产者消费者模型,将请求静态的放入队列中,由调度器反复遍历找出所有的可捎带请求,确保无遗漏。

    第七次作业

     本次作业为了保险起见,避免出现第二次的问题,又采用了生产者消费者模型,以及简单的先来先服务策略。RequestQueue作为托盘,InputHandler作为生产者,WorkerThread作为消费者。

    新采用了WorkerThread模式,重新封装了PersonRequest类,变成了Request类。Request中有一个方法execute()方法,是该指令执行的说明书,它返回一个Request列表,将该请求进行拆分,拆分为像第一次作业中那样的简单两个请求。

    WorkThread作为消费者从托盘中依次取出指令,按说明书调度电梯执行。SetUp类里面封装了三个电梯的停靠设置,提供了一个电梯是否停靠的方法。

    //由于采用最简单的实现方式,中测全部通过,强测只通过了一个点。

     二、程序度量

     复杂度分析,各方法的基本复杂度,模块设计复杂度和圈复杂度

    第五次作业

    第五次作业实现比较简单

    第六次作业

    第六次作业中Dispatcher中两个核心的方法都复杂度比较高。

    Dispatcher.run()实现的是对主请求的一次执行,在这一次执行中,将所有的捎带请求都执行,使请求列表为空。

    Dispatcher.serve()方法实现电梯到达一个楼层后,进行人员的服务,即上下电梯。

    确实,后期发现的bug主要也是出自这个类,bug在下面再讲。

    第七次作业

    Request.execute()方法中有许多if分支,通过判断来确定对一个请求的分解,过多分支增加了测试的难度,同时通过if……else……来分解请求过度依赖于楼层关于停靠的设置,扩展性不好。

    三、设计原则自查

    S(Single Responsibility Principle)

    每个类和方法都有明确的职责,这个原则执行的比较好,只是有的方法实现方式比较复杂,比如第六次作业中的Dispatcher.run()和第七次作业中的Request.excute()

    O(Open Close Principle)

    第五次作业中的Elevator.serve(),将电梯的运动(开门、进入、关闭、运动、开门、出去、关闭)耗时直接以魔数的形式写明,当电梯属性有改变或是有多个电梯时,不能满足要求。后几次作业已经将电梯与调度分离,并为电梯设置了构造方法。

    第七次作业中的Request.excute(),如果电梯的楼层停靠要求发生变化,则代码也需改动,对请求的分解过度依赖本次具体的停靠设置。

    L(Liskov Substitution Principle)

    这三次作业迭代时没有通过继承的方式,尤其第七次应该继承第六次作业实现多个电梯的可捎带,第七次作业又使用了第五次的先来先服务。作业都是复制了上一次的代码然后删删改改,直接重构,内部没有继承关系。以后作业中应考虑使用继承等机制,增加代码重用度。

    I(Interface Segregation Principle)

    这三次作业中没有实现接口。可以将Request设置为接口,待实现方法为excute,以后不同类型的需求可实现Request接口,自定义自己的说明书。

    D(Dependency Inversion Principle)

    第七次作业中的Request.excute(),太过依赖具体的停靠设置,可扩展性不好。

    四、自己程序的bug

    主要分析第六次作业中存在的问题:

    1.人员进出bug:当电梯停靠某一楼层时,会出现电梯中没有此人却出去或是此人已经进入电梯第二次进入的情况。原因是在上下电梯时扫描的是符合该楼层的请求,但是没有对该请求人的状态进行判断。修改方法是在电梯中加一个hashmap,以id作为key,记录电梯中是否有此人。

    2.请求的分析与执行不同步。有时候判断一个请求可以捎带,但是将该请求加入可捎带队列后却没有被执行。原因是当判断时,该请求是可以捎带的,当把该请求加入请求队列后,电梯已经运行了一段时间,所以变的不可捎带了,这样也导致一次主请求执行完毕后,请求队列中的请求没有全部被执行完成。修改方法时在判断请求是否可捎带的部分加dispatcher对象锁,这样在判断时电梯的状态是确定的,加入该请求后仍然可捎带。

    3.电梯执行状态更新不当。当加入可捎带请求,电梯本次执行目的地楼层发生变化后,要即时获取该变化,之前使用了一个变量,例如x = elevator.getTermini(),之后便一直使用x,当x过期后也没有即时更新,直接使用elevator.getTermini()可以即时更新。

    4.主请求与非主请求混淆。加入一个主请求,设置电梯从当前楼层到请求楼层接人,再到目标楼层完成任务,设置两次运动的起点和终点。加入一个非主请求,是当前请求可在电梯当前方向上可有被捎带,可能改变的是电梯一次运动的起点和终点。所以这两种请求加入队列应该使用不同的两种方法,混淆使用会出现错误。

    5.设计bug:将所有请求都开一个分配线程无法保证可捎带的请求能够一次性全部被捎带。

    五、心得体会

    1、在设计时应该多下功夫,应该明确到每个类的职责和类方法的职责,以及考虑一下实现的复杂度。否则没有设计清楚就开始写会出现类与类之间的职责不明确,耦合性高、使用一种实现发现越写越复杂,最后完成蕴藏着许多bug的情况。

    2、线程安全不应该是无脑synchronized,这样不仅对线程效率有影响,更重要的是对学习线程安全没有帮助,应该分析对象的共享情况,具体分析。

  • 相关阅读:
    c# winfrom 查看网络图片
    C# winfrom 读取txt文本内容
    c# winfrom 下载网页源代码
    c# winfrom 下载网络资源
    c# 换行符
    内存映射问题
    APUE 书中 toll 函数
    [P3387] 【模板】缩点
    [P5960] 【模板】差分约束算法
    [CF1442C] Graph Transpositions
  • 原文地址:https://www.cnblogs.com/pandaaud/p/10762847.html
Copyright © 2011-2022 走看看