一幢21层的大厦,有4部电梯,乘客的体重:平均70kg,最大120kg,最小40g)。
其他的常量包括:电梯的速度,电梯门开关时间,乘客进出电梯的时间。
大厦的楼层为-1,0,...,20,-1层是地下停车场,1层是大厅。以下是4部电梯的参数:
电梯编号 | 服务楼层 | 乘客人数限制 | 重量限制 |
---|---|---|---|
1 | 所有楼层 | 10 | 800kg |
2 | 1-10层 | 10 | 800kg |
3 | -1,1-10层 | 20 | 1600kg |
4 | -1,1,11-20层 | 20 | 2000kg |
-
PSP表格
-
Information Hiding, Interface Design, Loose Coupling
信息隐藏:为避免一个类被其他类赋值属性,可以将类中的属性设置为私有哦,通过get和set函数过去和设置,idea提供了快速设置get和set函数的功能。
接口设计:一个好的接口能够提供给后面的程序设计一个良好的框架,通过设置接口可以迅速的了解电梯、乘客、调度器类的方法与属性,这有利于软件测试的展开
松耦合:本次项目实际中,乘客与电梯时通过调度器来调度的,而调度函数也只需要传参,不存在内容上的耦合,因此在对程序做出改动时,不用担心会破坏其它地方的代码。这种类与类之间依赖性低的设计方法,使一个类与另外一个类隔开了,只是通过消息来联系。
-
模块接口设计与实现过程
类:实体类
线程类:
项目中所使用的的类大概分为实体类、线程类、其他类
实体类包括:电梯类Lift,乘客类Person,调度器类Despatcher
线程类包括:电梯线程LiftThread,调度器线程DespatcherThread,监视线程monitorThread
其他类包括:图形界面类MainInterface,主类Main
另外,在第二部分-实现基准Bus算法中,使用BusLiftThread和BusDespatcher分别代替LiftThread与Despatcher
主要接口与类间关系:
Lift封装了基本的电梯操作函数
addPerson,传入Person实例,实现乘客登上电梯
removePerson,传入序号,实现特定乘客离开电梯
shouldOpen,判断当前楼层是否有乘客要搭载当前电梯,参与决定是否开电梯门
canNotService,传入一乘客,判断乘客是否能登上电梯(取决于电梯停靠楼层,电梯的人数与重量限制)
LiftThread是对Lift进行的加工,内部设置时间通过循环不断增加,以时间推移模拟电 梯的运动,内部的run函数是电梯运动模块,通过电梯当前的状态(-2~2)决定电梯的 行动逻辑与乘客的退出逻辑
Despatcher是调度器,被Main函数调用,负责Person与LiftThread的连接,当调度器接 收一个Person实例时,它根据乘客的当前楼层。目标楼层,和当前电梯运行状态为乘客 调度 一个合适的电梯。
Despatcher的entryFloor函数是调度模块的核心函数,当一个满足一下几种条件之一 时,该电梯成为乘客的可用候选
-
电梯处于等待状态
-
电梯的目的地与运动方向都是向上,乘客的目标方向向上且在电梯上方
-
电梯的目的地与运动方向都是向下,乘客的目标方向向下且在电梯下方
-
电梯的最终目的地向下但临时目的地向上(假如电梯在1楼,一个被调度给该电梯的乘客在5楼且要去-1楼,则电梯最终目的地为-1,临时目的地为5)乘客的目标方向向下且所处楼层介于电梯当前楼层与临时目的地之间
-
电梯的最终目的地向上但临时目的地向下(假如电梯在1楼,一个被调度给该电梯的乘客在5楼且要去-1楼,则电梯最终目的地为-1,临时目的地为5)乘客的目标方向向下且所处楼层介于电梯当前楼层与临时目的地之间
成为可用候选的电梯经过优先级比较后决定最终调度给乘客的电梯,优先级:3号>2号>1号>0号
若当前所有电梯都不是该乘客的可用候选则Despatcher创建一个DespatcherThread
DespatcherThread的调度模块与Despatcher大致相同,只是当无可用候选时DespatcherThread会循环为该乘客进行请求,而Despatcher接收下一个由Main传来的请求
本项目的独到之处是实现的调度器函数的动态增减,若一乘客能在第一次请求被顺利调度则只有主Despatcher在启动,只有暂时无可用候选电梯时才会临时创建DespatcherThread循环请求。
-
UML图
电梯调度系统活动图
-
Design by Contract
Contracts for Java 是一个开源的Java工具,可让开发者对代码进行注释来约束代码的先决条件、后置条件和不变量。
项目中需要对数据范围做出限制的属性主要有Lift.currentWeight,Lift.currentPeopleNum和Lift.currentFloor,前两者需分别小于等于电梯固有的载重上限与成员上限,而后者需大于等于-1层小于等于20层
我们在开发中没有直接使用Contract插件,但沿用了Contract的思想,对于Lift.currentWeight与Lift.currentPeople,设置了canNotService函数保证只有在当前乘客进入电梯后不超过限制时才提供调度,对于Lift.currentFloor,由于电梯的最终目标是最后一个乘客的目标楼层决定的,而乘客目标楼层不可能超出范围,所以Lift.currentFloor也实现了约束,不可能超出范围。
-
代码规范与异常处理
-
代码规范:
注释规范:在代码中的重要模块写注释
命名规范:类名首字母和单词首字母大写,类的方法名与属性名使用驼峰式命名
-
异常处理:
在代码编写、测试过程中出现不影响整体运行的异常时,记录下来、用try-catch捕获,等待程序编写完成、进入代码优化阶段时根据idea弹出的警告与修改建议进行优化
-
界面详细设计过程
1.设计界面构成
在进行用户界面的设计时,为了使用户能清晰的了解4个电梯的显示情况,因此设计了能实时显示电梯楼层的显示按钮用户能通过明显的颜色变化直观的了解,此外由于电梯存在人数限制,和数量限制,为了减少用户在乘坐电梯时的负担,设计了能实时显示各个电梯的当前人数和当前重量,在输入方面,用户只需要输入当前层数和目标层数并点击确认按钮就能轻松实现电梯的乘坐。
2.代码实现界面
主要运用了Java swing对该界面进行实现
主界面类:MainInterface(实现界面的整体构架)
监视线程类:monitorThread,对Thread线程类进行继承(实现电梯的动态显示)
在MainInterface中对主界面进行整体设计,监视线程主要通过监视调度器线程的电梯状态,实时回馈电梯的运行状态。
3.界面构成
-
界面模块与其它模块的对接
界面模块与主界面和监视线程的对接,在主模块中生成界面类,将界面的变量作为参数传入监视模块的构造函数,从而达到监视类对主模块的动态修改。
在电梯运行时,监视模块实时反馈信息和数据。体现了MVC设计模型,用户在视图中得到反馈的信息和数据,监视模块和调度器实现用户的需求。
-
结对过程照片
由于是寒假线上合作,结对过程照片以腾讯会议截图代替
-
合作方式 优缺点,三明治方法
结对过程中我们的合作方式是先共同确定类图,达成共识,随后按算法逻辑和图形界面两部分分工。博客的撰写采用先明确分工后共享文档共同编辑的方法。
三明治谈话法,是指对某个人先表扬、再批评、接着再表扬的一种谈话方式。用在批评、激励方面,效果显著。采用这种方法可以委婉指出队伍中的缺点并让队员接受,有利于项目质量的优化和效率的提升
胡梦君同学在结对编程过程中展现出效率高、能思考、善沟通的优点 缺点是细心程度有待加强
-
单元测试
-
等价类划分
-
测试数据
12.其他收获
1).对java编程进行了更加深入的了解
2).项目全程采用驼峰式命名,这使我们避免了名称混淆
3).对java swing进行了初步尝试
4).项目中采用了多线程方式,这使我们对之前学习到的操作系统知识进行了巩固
5).参与并完成了一整个软件设计流程,学习到了许多编程之外的知识(代码审查、版本控制、需求分析与接口设计等)