面向对象第二单元总结——魔鬼电梯

写在前面

OO课程已通过半,过去的第二单元主要是训练了咱们的多线程设计,以电梯为载体,步步深刻,层层递进。本单元我学到了:java

  • 如何作一个线程安全的设计。
  • 如何去合理地使用Java的锁机制。

下面我将以三次做业为例,具体谈一下个人收获。python

第五次做业

此次做业的要求是写一个傻瓜式调度的电梯,笔者也是按指导书去写的,总体写起来很轻松,也算入门了Java多线程的写法、共享对象的使用以及锁的使用。算法

代码规模

图片名称

类图

图片名称

UML

图片名称

第一次的业务逻辑很清晰,就是简单的生产者-消费者模型,输入类为生产者,电梯类为消费者,共享对象是调度器里的请求队列。能够很简单地实现傻瓜电梯的功能,也不容易出bug。安全

复杂度分析

Method ev(G) iv(G) v(G)
elevator.Elevator.Elevator(Scheduler) 1 1 1
elevator.Elevator.moveToFloor(int) 1 2 2
elevator.Elevator.personIn(int) 1 2 2
elevator.Elevator.personOut(int) 1 2 2
elevator.Elevator.run() 3 3 3
elevator.Main.main(String[]) 1 1 1
elevator.RequestIn.RequestIn(Scheduler) 1 1 1
elevator.RequestIn.run() 3 4 4
elevator.Scheduler.Scheduler() 1 1 1
elevator.Scheduler.addRequest(PersonRequest) 1 1 1
elevator.Scheduler.getQueue() 1 1 3
elevator.Scheduler.getRequest() 1 3 3
Class OCavg WMC
elevator.Elevator 1.4 7
elevator.Main 1 1
elevator.RequestIn 2 4
elevator.Scheduler 1.75 7

能够看到第一次业务逻辑简单,代码的复杂度也相对小一点,只有电梯的运行方法稍显复杂,这也是情理之中的。多线程

SOLID原则分析

本单元做业笔者未使用继承和接口,因此如下只分析SRP和OCP。架构

单一责任原则(SRP)

笔者的设计符合SRP原则,输入类只负责输入,电梯只负责运行,调度器只负责存储请求队列。工具

开放封闭原则(OCP)

笔者在第一次做业中暂未考虑扩展性问题。性能

Bug分析

公测

笔者的程序在公测中未被发现bug。学习

互测

笔者的程序在互测中未被发现bug。测试

测试方法

随机生成测试数据,用java程序按时间点向电梯输入请求,获得输出后用python程序检查最终的电梯状态和人员状态,看一下调度是否正确。

第六次做业

本次做业的要求是写一个ALS调度算法的电梯,因为此次做业加入了性能分评测,故笔者写的电梯是Look调度算法。此次做业的逻辑稍显复杂,怎么去避免死锁发生,怎么去写一个线程安全的架构是咱们考虑的重点。

代码规模

图片名称

类图

图片名称

UML

图片名称

第二次做业相比第一次做业,只增长了捎带需求,而为了适应多电梯的扩展,我对电梯系统作了重构,首先,增长Elevatorbuild类,负责生成多部电梯,而后Elevator按Look调度算法运行,每到一层楼都和Scheduler调度器进行一次交互,进行接送乘客。由于是单电梯,因此本次的调度器职责就是把收到的指令分配给电梯。

复杂度分析

Method ev(G) iv(G) v(G)
Main.main(String[]) 1 1 1
elevatorsystem.RequestInput.RequestInput(Scheduler) 1 1 1
elevatorsystem.RequestInput.run() 3 4 4
elevatorsystem.Scheduler.addRequest(PersonRequest) 1 1 1
elevatorsystem.Scheduler.getScheduler() 1 1 3
elevatorsystem.Scheduler.setRunning(Boolean) 1 1 1
elevatorsystem.elevator.Elevator.Elevator() 1 1 1
elevatorsystem.elevator.Elevator.check() 1 6 8
elevatorsystem.elevator.Elevator.close() 1 2 2
elevatorsystem.elevator.Elevator.doWait() 1 2 2
elevatorsystem.elevator.Elevator.elevatorStop() 1 6 11
elevatorsystem.elevator.Elevator.getArrival() 1 1 1
elevatorsystem.elevator.Elevator.getDirection() 1 1 1
elevatorsystem.elevator.Elevator.getFloor() 1 1 1
elevatorsystem.elevator.Elevator.getNextRequest() 1 4 4
elevatorsystem.elevator.Elevator.getRequestNum() 1 1 1
elevatorsystem.elevator.Elevator.getRunning() 1 1 1
elevatorsystem.elevator.Elevator.getStatus() 1 1 1
elevatorsystem.elevator.Elevator.isDown(PersonRequest) 1 1 1
elevatorsystem.elevator.Elevator.isUp(PersonRequest) 1 1 1
elevatorsystem.elevator.Elevator.load() 1 6 6
elevatorsystem.elevator.Elevator.lookDown() 4 2 4
elevatorsystem.elevator.Elevator.lookUp() 4 2 4
elevatorsystem.elevator.Elevator.moveDown() 1 2 3
elevatorsystem.elevator.Elevator.moveUp() 1 2 3
elevatorsystem.elevator.Elevator.open() 1 2 2
elevatorsystem.elevator.Elevator.run() 6 16 17
elevatorsystem.elevator.Elevator.setArrival(ArrayList ) 1 1 1
elevatorsystem.elevator.Elevator.setDirection(int) 1 1 1
elevatorsystem.elevator.Elevator.setFloor(int) 1 1 1
elevatorsystem.elevator.Elevator.setInf(int) 1 1 1
elevatorsystem.elevator.Elevator.setRequestNum(Integer) 1 1 1
elevatorsystem.elevator.Elevator.setRunning(Boolean) 1 1 1
elevatorsystem.elevator.Elevator.setStatus(Integer,ArrayList ) 1 1 1
elevatorsystem.elevator.Elevator.setSup(int) 1 1 1
elevatorsystem.elevator.Elevator.unload() 1 3 3
elevatorsystem.elevator.ElevatorBuild.InitOne() 3 2 3
elevatorsystem.elevator.ElevatorBuild.getElevatorBuild() 1 1 3
elevatorsystem.elevator.ElevatorBuild.getElevatorOne() 1 1 1
elevatorsystem.elevator.ElevatorBuild.run() 1 1 1
Class OCavg WMC
Main 1 1
elevatorsystem.RequestInput 2 4
elevatorsystem.Scheduler 1.67 5
elevatorsystem.elevator.Elevator 2.17 65
elevatorsystem.elevator.ElevatorBuild 2 8

不出所料,仍然是Elevator的运行方法和每层楼的接送乘客方法复杂度较高。

SOLID原则分析

单一责任原则(SRP)

笔者的设计符合SRP原则,输入类只负责输入,电梯的Builder类只负责生成电梯,电梯只负责运行已收到的请求,调度器只负责将收到的请求分给电梯。

开放封闭原则(OCP)

笔者在此次做业中充分考虑了向多电梯的可扩展性,新增ElevatorBuild类,负责根据不一样参数生成不一样种类的电梯,因此这个架构在第三次做业中得以沿用。

Bug分析

公测

笔者的程序在公测中未被发现bug。

互测

笔者的程序在互测中未被发现bug。笔者发现其余同窗的一个bug,在某种状况下,他的电梯会一直向上或向下运行而不会停下(所谓的飞天遁地),缘由应该是对边界楼层和电梯转向的处理不严密。

测试方法

笔者沿用了第一次做业的测试工具,定时投放+输出数据正确性检测。

第七次做业

最后一次做业增长了多电梯(3个),而且每一个电梯的可到达楼层不一样,须要考虑换乘的状况,总体难度较大,而且对线程稳定性的要求也很高。笔者的设计是采用三台Look调度算法的电梯,每一个电梯只负责本身请求队列里的指令,须要换乘的指令会在指令输入的时候被拆分红两条指令,在第一条指令执行完毕后将第二条指令加入请求队列。

代码规模

图片名称

类图

图片名称

UML

图片名称

能够看到,对于笔者的架构,从第二次做业到第三次做业,几乎只须要修改调度器,其余部分不作改动,因此笔者此次做业完成地也比较轻松。

复杂度分析

Method ev(G) iv(G) v(G)
Main.main(String[]) 1 1 1
elevatorsystem.RequestInput.RequestInput(Scheduler) 1 1 1
elevatorsystem.RequestInput.run() 3 4 4
elevatorsystem.Scheduler.InitChange(ArrayList<ArrayList >) 1 2 2
elevatorsystem.Scheduler.Scheduler() 1 1 1
elevatorsystem.Scheduler.addRequest(PersonRequest) 1 10 10
elevatorsystem.Scheduler.checkStatus(char,ArrayList ) 6 9 9
elevatorsystem.Scheduler.createMapA() 1 5 10
elevatorsystem.Scheduler.createMapB() 1 5 8
elevatorsystem.Scheduler.createMapC() 1 5 6
elevatorsystem.Scheduler.doWait(Object) 1 2 2
elevatorsystem.Scheduler.getChange() 1 1 1
elevatorsystem.Scheduler.getLock() 1 1 1
elevatorsystem.Scheduler.getMap(char) 3 1 3
elevatorsystem.Scheduler.getQueue(char) 3 1 3
elevatorsystem.Scheduler.getRequestNum(char) 3 1 3
elevatorsystem.Scheduler.getRunning() 1 1 1
elevatorsystem.Scheduler.getScheduler() 1 1 3
elevatorsystem.Scheduler.lock() 1 1 1
elevatorsystem.Scheduler.mapInit(HashMap<Integer, ArrayList >) 3 2 3
elevatorsystem.Scheduler.removeRequest(PersonRequest) 1 1 4
elevatorsystem.Scheduler.requestMapInit(HashMap<Integer, ArrayList >) 3 2 3
elevatorsystem.Scheduler.setRunning(Boolean) 1 1 1
elevatorsystem.Scheduler.takeApart(PersonRequest) 1 10 10
elevatorsystem.Scheduler.unLock() 1 1 1
elevatorsystem.elevator.Elevator.Elevator(Scheduler,Object) 1 1 1
elevatorsystem.elevator.Elevator.addAvailableFloor(int) 1 1 1
elevatorsystem.elevator.Elevator.check() 1 7 9
elevatorsystem.elevator.Elevator.close() 1 2 2
elevatorsystem.elevator.Elevator.doWait(Object) 1 2 2
elevatorsystem.elevator.Elevator.elevatorStop() 1 5 10
elevatorsystem.elevator.Elevator.getArrival() 1 1 1
elevatorsystem.elevator.Elevator.getDirection() 1 1 1
elevatorsystem.elevator.Elevator.getFloor() 1 1 1
elevatorsystem.elevator.Elevator.getNextRequest() 1 4 4
elevatorsystem.elevator.Elevator.isDown(PersonRequest) 1 1 1
elevatorsystem.elevator.Elevator.isUp(PersonRequest) 1 1 1
elevatorsystem.elevator.Elevator.load() 3 6 7
elevatorsystem.elevator.Elevator.lookDown() 4 2 4
elevatorsystem.elevator.Elevator.lookUp() 4 2 4
elevatorsystem.elevator.Elevator.moveDown() 1 2 3
elevatorsystem.elevator.Elevator.moveUp() 1 2 3
elevatorsystem.elevator.Elevator.open() 1 2 2
elevatorsystem.elevator.Elevator.run() 6 17 19
elevatorsystem.elevator.Elevator.setArrival(ArrayList ) 1 1 1
elevatorsystem.elevator.Elevator.setDirection(int) 1 1 1
elevatorsystem.elevator.Elevator.setFloor(int) 1 1 1
elevatorsystem.elevator.Elevator.setInf(int) 1 1 1
elevatorsystem.elevator.Elevator.setMaxContain(int) 1 1 1
elevatorsystem.elevator.Elevator.setMoveTime(int) 1 1 1
elevatorsystem.elevator.Elevator.setName(char) 1 1 1
elevatorsystem.elevator.Elevator.setOpenTime(int) 1 1 1
elevatorsystem.elevator.Elevator.setSup(int) 1 1 1
elevatorsystem.elevator.Elevator.unload() 1 7 8
elevatorsystem.elevator.ElevatorBuild.ElevatorBuild(Scheduler) 1 1 1
elevatorsystem.elevator.ElevatorBuild.InitA() 1 3 5
elevatorsystem.elevator.ElevatorBuild.InitB() 1 3 4
elevatorsystem.elevator.ElevatorBuild.InitC() 1 3 3
elevatorsystem.elevator.ElevatorBuild.getElevatorA() 1 1 1
elevatorsystem.elevator.ElevatorBuild.getElevatorB() 1 1 1
elevatorsystem.elevator.ElevatorBuild.getElevatorBuild(Scheduler) 1 1 3
elevatorsystem.elevator.ElevatorBuild.getElevatorC() 1 1 1
elevatorsystem.elevator.ElevatorBuild.run() 1 1 1
Class OCavg WMC
Main 1 1
elevatorsystem.RequestInput 2 4
elevatorsystem.Scheduler 3.14 69
elevatorsystem.elevator.Elevator 2.45 71
elevatorsystem.elevator.ElevatorBuild 1.89 17

能够看到,调度器的添加请求方法和拆分请求方法,以及电梯的run方法复杂度很高。结合代码不难看出复杂度高的缘由。调度器的添加请求方法须要作这么几件事:

  • 判断电梯是否可直达,是否满载,若是未满载而且可直达则加入请求;
  • 若是所有满载则选择一部可直达的电梯加入请求;
  • 若是不可直达,则调用拆分请求方法进行指令拆分和添加。

这其中有很复杂的条件语句,因此方法复杂度略高。

至于电梯的run方法,是负责整个电梯运行逻辑的,因此复杂度略高也合乎情理。

SOLID原则分析

单一责任原则(SRP)

笔者的设计符合SRP原则,输入类只负责输入,电梯的Builder类只负责生成电梯,电梯只负责运行已收到的请求,调度器只负责接收和拆分请求,并分配给不一样的电梯。

开放封闭原则(OCP)

笔者在此次做业中保留了对更多电梯的可扩展性,经过ElevatorBuild类可进行扩展。可是本次做业个人调度器是每一个电梯一个请求队列,因此扩展起来比较麻烦,我想若是设计成全部电梯共用一个请求队列的话,应该会更加便于扩展。

Bug分析

公测

本次公测笔者的程序炸了不少点,缘由是有一个条件语句加错了位置,更改后可稳定经过所有测试点。

互测

笔者找到其余同窗不少bug,有程序没法结束的,有电梯超载的,有运行时间过长的,还有乘客未送到指定位置的。毕竟是C组,这么多bug也就不足为奇了。

总结

多线程设计让我明白了如下几点:

  • 必定要充分思考之后再动手写代码,否则在多线程设计中出了bug是很难调试的。代码不规范,debug两行泪。
  • 不要盲目地去使用锁,synchronized当然好用,可是也不能滥用,要明确锁的是哪一个对象,哪一个类。synchronized使用也是有技巧的,有时候一个方法里面只须要锁住一个对象就能够,不必把整个方法都锁起来,这样会形成效率的下降。
  • 每写好一个版本后,都须要对应充分的测试,一个优秀的工程设计师是兼顾正确性和高性能的,不要为了追求一点点的性能而在设计上产生漏洞,更不要目测程序没问题就不去作充分的测试。这是我在第三次做业中学到的,没测试充分,致使bug没被发现,因此炸掉了强测。

但愿在之后的学习中,我能够更好地掌握测试方法,写出更加完美的程序。

相关文章
相关标签/搜索