学习目标:
按照文档实现傻瓜电梯,具体要求入下:
1、每组2-4人
2、完成公测用例。如果未完成公测用例需要说明完成公测用例第几到第几个,未完成那几个。
3、注意增加合理代码注释。
4、注意变量,函数和类名的命名规范。命名原则:能一眼识别出来,例如 年龄 iAge 体现变量类型和逻辑意义。
5、注意电梯临界时间,如果计算时间和公测用例和文档不一致,如果能给出合理解释需提供相关文档说明。
6、注意合理的封装,例如某些公共功能如何封装更合理。
要求:
一:要求;
OO 第二次作业要求
2018
1. 作业目标
通过设计一个简单的单部电梯运行控制系统,要求设计者以面向对象程序的 设计方式来实现电梯控制系统的具体功能。
2. 作业内容和成果物
2.1 业务背景
1) 电梯定义 电梯是服务于规定楼层的固定式升降设备。垂直升降电梯具有一个轿厢,运 行在至少两列垂直的或倾斜角小于 15°的刚性导轨之间。台阶式自动扶梯和滚 梯不在本次作业考虑范围之内。
2) 电梯的简单使用方法(不考虑紧急情况和复杂情况): 载人电梯都是微机控制的智能化、自动化设备,不需要专门的人员来操作驾 驶,普通乘客只要按下列程序乘坐和操作电梯即可。
i) 在乘梯楼层电梯入口处,根据自己上行或下行的需要,按向上或向下的 箭头按钮,只要按钮上的灯亮,就说明你的呼叫已被记录,只要等待电 梯到来即可。
ii) 电梯到达目标楼层停下开门后,先让轿厢内人员走出电梯,然后呼梯者 再进入电梯轿厢。进入轿厢后,根据你需要到达的楼层,按下轿厢内操 纵盘上相应的数字按钮。同样,只要该按钮灯亮,则说明你的目标楼层 已被记录;此时不用进行其他任何操作(在到达目标楼层之前,反复按 该目标楼层的按钮为无效操作),一旦电梯门关闭之后,电梯就会开始 运动,只要等电梯到达你的目标层停靠开门即可。
iii) 电梯行驶到你的目标层后会自动开门,此时按顺序走出电梯即结束 了一次乘梯过程。
2 / 8
2.2 作业电梯系统基本描述
1) 本作业的电梯为一个运行在 10 层楼的电梯,电梯可在每层经停。楼层计数 采用中国式楼层计数,即 1 层显示为 1,2 层为 2,依次类推,直至顶层显示 为 10。
2) 每个楼层都有一组电梯运行请求按钮(下面简称为楼层按钮),即上行请求 按钮和下行请求按钮。如果乘客按上行请求按钮时,则表示想搭乘电梯去往 上面的楼层;反之,则表示想搭乘电梯去往下面的楼层。规定最底层(1 层) 只有上行请求按钮,最顶层(10 层)只有下行请求按钮,中间楼层同时有上 行和下行两个方向请求按钮。
3) 一个电梯箱体(剩余文档简称电梯)内有对应每个楼层的目的地请求按钮(下 面简称目标楼层),一般显示相应楼层的层数。
2.3 电梯基本运行规则
1) 程序运行开始或重置时设置电梯停靠在一层;
2) 几个名词解释: a) 电梯运行状态:从电梯启动时刻(此时速度>0),到电梯运动停止时(此 时速度刚刚为 0)的运行状态。(启动时刻,运行停止时刻],期间运行 速度始终大于 0。 b) 电梯开关门状态:电梯静止时,从门打开时刻(门开始动作),到门完 全关闭时刻(门刚刚停止动作)时的状态。(门打开时刻,门关闭时刻] c) 电梯停留状态:电梯停在某层,且门长时间处于关闭状态。(门关闭时 刻,门准备打开时刻或电梯准备启动时刻]
3) 一个楼层按钮同一时刻只能发出一个上行或下行请求。电梯未到本楼层的时 候,某个请求按钮变亮后,再按不会产生实际效果,但是发出上行请求后可 以再发出下行请求,反之亦可,这视为两个不同的请求,执行完一个后另一 个仍需执行。在电梯到达某楼层处于开关门状态时,该楼层的多个同向请求 只认为是一个请求。当电梯关门动作完成后(含门完全静止的那一刻),可 以再产生新的上下行请求;
4) 电梯内的一个目标楼层按钮只能发出对应目标楼层的请求,一旦发出某个目 标楼层请求后,在电梯到达该楼层并完成关门动作前(包括关门完毕时刻), 3 / 8 目标楼层与该按钮楼层相同的多个电梯内请求被认为是一个请求。当电梯关 门结束后,可以再发出任意目标楼层请求。
5) 所有请求按照请求发出的时间顺序被电梯系统管理和调度,按照时间上先来 先服务的策略(First Arrived First Served,FAFS)进行调度。
6) 如果电梯同时收到了电梯内请求和楼层请求时,则按照输入时的请求排列顺 序执行。 7) 本次作业的电梯系统采用傻瓜式调度策略:控制系统不断扫描请求队列,按 照 FAFS 策略取出待响应请求,只有当该请求被执行完毕之后,才会尝试调 度下一个请求。请注意此处不要按照常见的电梯运行调度策略进行优化设计, 不要合并“顺路”的请求。如从 2 层去 8 层期间,未到 6 层时有 6 层的上行 请求,应先处理完 2 层到 8 层的请求,再处理 6 层的请求;或者 2 层到 8 层 时,中间又发出了去 6 层的请求,同样先执行完到 8 层的动作,再处理去 6 层的请求。
3. 作业内容和成果物
3.1 作业内容
实现一个符合 2.2 节和 2.3 节所描述的电梯运行调度 java 程序。
3.2 提交内容
1)java 语言程序(java 程序文件);
2)程序说明文档(Readme),内容包括:
a) 电梯调度策略和程序功能说明;
b) 程序运行所需环境和运行指令规范;
c) 程序的输入说明,包括标准输入格式、输入限制和遇见输入错误时的 响应信息;
d) 程序计算结果的输出规格,以及可预见的运行错误响应信息;
3)程序中若干类的说明文档(Readme,word 文件),详见 4.4 节。
4. 作业要求和限制
4.1 输入规范 用户输入为按照请求产生时间排序的请求序列(注意:可以输入时间相同的 两个请求,排在前面请求被优先执行),序列通过字符串表示;
4 / 8
请求分为两类:一类是楼层请求,一类是电梯内请求。
楼层请求格式为:(FR, m, UP/DOWN, T),其中 FR 为楼层请求标识,m 为发 出请求的楼层号,UP 为向上请求,DOWN 为向下请求,T 为发出时刻。(注释:相 当于请求者在楼道里的某楼层按“上行”或“下行”键) 电梯内请求格式为:(ER, n, T),其中 ER 为电梯内请求标识,n 为请求前往 的目标楼层号,T 为发出时刻。(注释:相当于人在电梯里按一个目标楼层号) 所有的逗号应采用 ASCII 字符集中的逗号“,”,而不是中文字符逗号“,”。 请求之间必须通过换行进行分隔,两条请求之间不允许有空行。一条请求的内部 元素之间可以有空格,要求程序能够自动过滤。 运行规则: T 为请求产生的相对时刻,第一个请求的 T 值必须设置为 0,否则视为一个 crash 错误!设电梯运行一个楼层距离的时间消耗为 0.5s;达到楼层后一次开关 门动作时间消耗为 1.0s。 合法的请求产生时刻为非负整数(要求最大为 4 字节的非负整数,格式不得 为-0),n,m 为 1~10 之间(包含)的正整数。 不正确的标识符,不正确的方向,不正确的数字范围,多余的其他非允许字 符,均认定为不合法输入,即无效输入。 特别地,对于 FR 标识符,1 楼的 DOWN 和 10 楼的 UP 也认为是无效输入。 在一行内只能输入一条请求,一行内输入多条请求将导致 OJ 无法识别!输 入全部请求后,必须再键入 RUN 表示输入结束,回车后程序开始执行调度。 本程序中的 T 和 t 均为模拟值,即不要按照 T 和 t 的值作为真实时间来控制 运行输出。 参考输入样例(例 1): (FR,3,DOWN,0)
(FR,1,UP,1)
(ER,1,2)
(ER,6,4)
RUN
本次作业要求一次性输入所有请求,然后执行程序进行电梯调度并输出结果。 标准输入的请求是按照时间排序的,如果遇到一个乱序的请求,即请求产生时间 小于前面一个请求产生时间,则该请求直接被丢掉,继续处理下一个请求。 要求程序能够忽略相同的请求,包括产生时刻相同的相同请求和产生时刻不 同但是实质上相同的请求。详情见 2.3.3 及 2.3.4 中的规定。
例 2: (FR,3,DOWN,0) (FR,3,DOWN,1) RUN 根据前面的运行规则,例 2 中的第二条请求发出时第一条请求还没有执行结 束,相当于楼梯按钮仍处于“按下状态”(即电梯尚未到达 3 层,而请求者多次 按了“下行按钮”),所以第二条请求与第一条请求实质上相同。
例 3: (FR,3,DOWN,0) (FR,3,DOWN,1000) RUN 第二条请求执行时,按照运行规则此时电梯已到 3 层,该请求相当于是同层 请求(即电梯停在本层),应执行一次开关门动作。对于电梯内请求的类似情况 同理。 本次作业不要求楼层请求和电梯请求的顺序符合真实情况(所谓真实情况, 即实际情况中电梯中如果没有乘客,不会产生电梯内请求,但本次作业做简化考 虑,任何顺序的楼层和电梯内请求都可被接受) 本次作业不允许使用文件作为输入。 除黑体和红色字表明的强制规定外,对于更多的细节的输入规范,如与文档 冲突,请在 readme 说明文档(PDF 文件)中说明,若没有说明且与文档的冲突, 测试者有理由质疑。
4.2 输入方式 本程序运行要求为控制台或命令行输入,具体输入方式由程序设计者决定, 但是要求在说明文档中加以明确说明。
6 / 8
4.3 输出规范
程序的输出为按照时间排序的电梯运行状态描述,包括以下内容:电梯停靠 的楼层、停靠前的运动方向及停靠时刻(即电梯刚到达目标楼层由运动转为静止 状态,尚未执行开关门的时刻): 格式为:(n,UP/DOWN,t) 注意:一行只有一个输出结果,若一行内有多个输出结果,则 OJ 无法识别! 其中 n 为楼层号,UP/DOWN 为电梯运行方向(只显示 UP 或 DOWN 之一); t 为相对于第一个请求发生的时间。对于 t 的规格有如下要求:t 是保留小数点 后一位有效位的浮点数,如果 t=3,则要求输出为 3.0;如果 t=0,则要求输出 0.0。不允许出现-0.0 的格式。否则会导致 OJ 误判!。 有同层请求时(即电梯停在某层,此时有目标为该层的请求),则输出为: (n,STILL,t),此处 t 应考虑电梯执行一次开关门动作的时间。 输出必须使用 system.out 标准输出,否则会导致 OJ 误判! 例: 输入为(ER,1,0) 时,应输出(1,STILL,1.0)。解释:电梯停在 1 层,此时在电梯 内发出去 1 层的请求,则输出开关门一次后的时刻。 输入为(ER,3,0) (ER,3,5) 时,应输出: (3,UP,1.0) (3,STILL,6.0) 输出格式要求采用 UTF-8 标准。 对于本程序不能识别的输入内容,必须按照以下规格进行输出,否则按照错 误结果扣分! ERROR #所对应的错误输入内容 相关的输出规范要在说明文档(README)中加以说明。 其他未规定的地方可由编程者自行决定。但是需要在 readme 中说明
4.4 设计要求 本次作业不能使用 lamda 表达式编程!
7 / 8
必须要实现电梯、楼层、请求队列、调度器、请求这五个类,且类中不允许 出现 Public 属性。其中楼层类可能相对简单一些,不做具体要求,但是其他四 个类的具体实现必须单独编写说明文件,内容包括类属性和方法的简要说明。 电梯类 调度器类 请求队列类 请求类 发出指令 访问请求 管理请求 发出请求 楼层类 发出请求 command schedule 几个类之间的协作关系参考图
5. 其它说明事项
5.1 设计建议
(1)从输入读取请求
(2)构造请求对象
(3)加入到队列中
(4)启动调度
(5)记录电梯对象对请求的响应
5.2 错误处理 处理原则:
1) 如果发现输入请求序列不满足时间排序要求,则输出无效输入提示(输出 格式为#开头的提示内容),并忽略不满足要求的请求,继续处理下一个 输入请求。
2) 遇到无效请求(包括格式或内容不符合要求的),需要给出无效输入提示 后,继续处理下一个输入请求直至结束。
3) 格式有误或数据无效(如楼层超过 10)的请求将被直接从输入请求序列中 去掉,并给出无效输入提示,但不影响继续对其他有效请求的调度处理。
8 / 8
4) 任何情况下,程序都不应 crash,要正常结束(exitcode=0)。
5.3 Tips 如何设计 3 个对象:电梯箱体、楼层、调度器,它们的定义是什么,有什么 (属性);它们能做什么,在什么情况下做(方法)。同理,分析另外 2 个内部 管理类。
6. 其他规定
1) 文档中粗体字体部分为强制要求。
2) 无效作业,以下四种情况视为无效作业。
(1)程序不能编译和运行;
(2)未使用 Java 语言;
(3)所编制的程序不是本次作业的内容。
(4)无法通过任何一个可以输出正常结果的公共测试案例;
二:代码目录:
三:代码实现及注释:
(1)Main.java:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) { //程序入口
Input input = new Input();
LinkedList<String> list = input.getList(); //new一个Input对象,在getlist()方法中输入命令command,放到list中
RequestsQueue requestsqueue = new RequestsQueue(); //new一个存储着用户名令的电梯请求队列对象
while(true) {
if(list.isEmpty()==true) list = input.getList(); //得到一个用户输入的有效命令
String[] command = input.inputchange(list.poll()); //从list中取出一个命令,并在inputchange()方法中把它简化一下
Elevator elevator = new Elevator(); //new一个电梯对象
if(command[0].equals("FR")) { //如果是对于在电梯外请求的用户的服务
System.out.println("***");
elevator.setmode(command[0]);
elevator.setaimfloor(Integer.valueOf(command[1]));
elevator.setdirection(command[2]);
elevator.setsendtime(Double.valueOf(command[3])); //把用户输入的命令中得出4个信息,并把相应的值存放到一个电梯对象里
requestsqueue.add(elevator); //把存有用户请求命令的电梯放到电梯请求队列里,等待服务
}
else if(command[0].equals("ER")) { //如果三对于在电梯内请求的用户的服务
elevator.setmode(command[0]);
elevator.setaimfloor(Integer.valueOf(command[1]));
elevator.setsendtime(Double.valueOf(command[2])); //把用户输入的3个信息,并把相应的值存放到一个电题对象里
requestsqueue.add(elevator); //把存有用户请求命令的电梯放到电梯请求队列里,等待服务
}
else System.out.println("ERROR"); //如果吉布森电梯外请求,也不是电梯内请求,那就是不合法的请求,直接输出错误即可
Solve.error(); //开始让Solve对象做解决操作,即根据相应的命令数据信息,得到运行输出结果
}
}
}
(2)Elevator.java:
public class Elevator {
private String mode; //电梯模式属性 ,ER,FR
private String direction; //电梯状态属性,up,DOWN,STILL
private Double sendtime; //用户发起请求时间
private Double endtime=0.0; //代你提结束云兄时间
private int nowfloor=1; //电梯当前所在的楼层,初始化为1
private int aimfloor; //电梯所要到的目标楼层
public void setmode(String mode) { //设置模式
this.mode = mode;
}
public void setdirection(String direction) { //设置状态
this.direction = direction;
}
public void setsendtime(double sendtime) { //设置发起请求时间
this.sendtime = sendtime;
}
public void setendtime(double endtime) { //设置电梯结束运行时间
this.endtime = endtime;
}
public void setnowfloor(int nowfloor) { //设置电题当前所在楼层
this.nowfloor = nowfloor;
}
public void setaimfloor(int aimfloor) { //设置电题所要到达的目标楼层
this.aimfloor = aimfloor;
}
//相面六个方法分别为得到电题相应的6个属性值的方法
public String getmode() {
return this.mode;
}
public String getdirection() {
return this.direction;
}
public Double getsendtime() {
return this.sendtime;
}
public Double getendtime() {
return this.endtime;
}
public int getnowfloor() {
return this.nowfloor;
}
public int getaimfloor() {
return this.aimfloor;
}
}
(3)Floor.java:
public class Floor {
private int maxFloor=10; //电梯最高楼层数殉国,初始化为10
private int minFloor=1; //电梯最低楼层数殉国,初始化为1
private void setmaxFloor(int maxFloor) { //设置最高楼层属性方法
this.maxFloor = maxFloor;
}
private void setminFloor(int minFloor) { //设置楼层最低属性方法
this.minFloor = minFloor;
}
private int getmaxFloor() { //得到最高楼层数
return this.maxFloor;
}
private int getminFloor() { //得到最低楼层数
return this.minFloor;
}
}
(4)Input.java:
import java.util.LinkedList;
import java.util.Scanner;
import javax.swing.text.html.HTMLDocument.Iterator;
public class Input { //该类的功能三让哟玫瑰湖输入命令和简化用户输入的命令
String[] inputchange(String s) { //命令简化
s = s.replaceAll(" ", ""); //去掉多余的空格
s = s.replaceAll("\\(|\\)", ""); //去括号
String[] s1 = s.split(","); //用都好将肌肤穿进行切割成String数组
return s1; //把结果返回
}
public LinkedList<String> getList(){ //让当前用户输入命令
Scanner sc = new Scanner(System.in);
LinkedList<String> list = new LinkedList();
while(true) {
String Command=sc.next();
if(Command.equals("RUN")) { //RUN输入结束的标志,breakjike1
break;
}else {
list.add(Command); //把该用户居输入的所有,imhling1寸放到一个链表里
}
}
return list; //返回该用户输入的所有命令
}
}
(5)RequestsQueue.java:
import java.util.LinkedList;
import java.util.Queue;
public class RequestsQueue {
private static Queue<Elevator> queue = new LinkedList<Elevator>(); //存放电梯请求队列,里面的元素三存放了用户命令信息的电梯
public void add(Elevator elevator) { //往队列里添加电梯元素
queue.offer(elevator);
}
public Elevator peek() { //查找元素
return queue.peek();
}
public Elevator poll() { //取出一个元素
return queue.poll();
}
}
(6)Solve.java:
public class Solve { //该类主要实现解决得出最后运行结果的功能呢个
//该方法主要三针对FR请求的相关计算和判断
public static String FRSolve(String direction, Integer aimfloor, double sendtime,Integer nowfloor,double endtime) {
Elevator elevator = new Elevator(); //new一个电梯对象
if ((aimfloor == 1 && direction.equals("DOWN"))) { //用户当前在1楼,但请求三项下,不合理
return "ERROR1";
} else if ((aimfloor == 10 && direction.equals("UP"))) { //用户当前在10楼,但请求三项上,不合理
return "ERROR2";
} else if (endtime > sendtime) { //拥护发出请求的时间电梯还在为上一个拥护据服务,没办法来服务放弃请求的用户,不合理
System.out.println(endtime + " " + sendtime);
return "ERROR3";
} else { //以下三合里的情况,有3种
if (aimfloor == nowfloor) { //目标楼层等于当前所在楼层,电梯相当于不懂,STILL
sendtime += 1f; //电梯结束运行时间要加上1,即电梯开关门时间
//更新电梯的相应属性值
endtime = sendtime;
elevator.setendtime(endtime);
elevator.setnowfloor(nowfloor);
return "STILL";
}
sendtime += Math.abs(aimfloor - nowfloor) * 0.5; //不精致的护士用电梯速度*楼层茶树,得到电梯为该用户服务所需要花费的时间按
//更新电梯结束运行时间按
endtime = sendtime;
if (nowfloor < aimfloor) { //电梯项上走了的情况,即UP
//做更新
nowfloor = aimfloor;
elevator.setnowfloor(nowfloor);
elevator.setendtime(endtime);
return "UP";
} else { //电梯项下走了的情况,即DOWN
nowfloor = aimfloor;
elevator.setnowfloor(nowfloor);
elevator.setendtime(endtime);
return "DOWN";
}
}
}
//该方法主要三针对ER请求的相关计算和判断
public static String ERSolve(Integer aimfloor, double sendtime) {
Elevator elevator = new Elevator();
double endtime = elevator.getendtime();
Integer nowfloor = elevator.getnowfloor();
if (sendtime > sendtime) { //电梯正忙,无法服务
return "ERROR3";
} else if (aimfloor < 0 || aimfloor > 10) { //楼层请求不再合里范围内
return "ERROR4";
}
if (nowfloor == aimfloor) { //电梯相当于不动,即STILL
//做更新
sendtime += 1f;
endtime = sendtime;
elevator.setnowfloor(nowfloor);
elevator.setendtime(endtime);
return "STILL";
} else {
sendtime += Math.abs(aimfloor - nowfloor) * 0.5;//计算花费时间爱女
//跟新结束运行时间
endtime = sendtime;
if (nowfloor > aimfloor) { //向下走了的情况,即DOWN
//做更新
nowfloor = aimfloor;
elevator.setnowfloor(nowfloor);
elevator.setendtime(endtime);
return "DOWN";
} else { //向上走了的情况,即UP
//做更新
nowfloor = aimfloor;
elevator.setnowfloor(nowfloor);
elevator.setendtime(endtime);
return "UP";
}
}
}
//该方法三对最终运行结果的输出
public static void error() {
RequestsQueue requestqueue = new RequestsQueue();
while (true) {
String sDirection;
if (requestqueue.peek() == null) {
break;
}
else if (requestqueue.peek().getmode().equals("FR")) { //FR模式的请求
// System.out.println(requestQueue.peek().getNowFloor());
Elevator elevator = requestqueue.poll();//从电梯请求队列里取出一个用户的命令
sDirection = Solve.FRSolve(elevator.getdirection(), elevator.getaimfloor(),
elevator.getsendtime(),elevator.getnowfloor(),elevator.getendtime()); //把电题里存放的命令信息作为参数出给FRSolve方法里进行结算和判断
if (sDirection.equals("ERROR1")) { //不合理的请求或无法服务的
System.out.println("ERROR");
System.out.println("#1楼不能下。");
} else if (sDirection.equals("ERROR2")) {
System.out.println("ERROR");
System.out.println("#顶楼不能上。");
} else if (sDirection.equals("ERROR3")) {
System.out.println("ERROR");
System.out.println("#命令发送时间有误。");
} else { //可以服务的情况
System.out.println( //把计算得到的结果进行输出
"(" + elevator.getnowfloor() + "," + sDirection + "," + elevator.getendtime() + ")");
}
}
else { //ER模式的请求
Elevator elevator = requestqueue.poll(); //求出一个用户命令
sDirection = Solve.ERSolve(elevator.getaimfloor(), elevator.getsendtime()); //调用ERSolve方法进行计算和判读啊
if (sDirection.equals("ERROR3")) { //不合礼的请求活无法服务的
System.out.println("ERROR");
System.out.println("#命令发送时间有误。");
} else if (sDirection.equals("ERROR4")) {
System.out.println("ERROR");
System.out.println("#命令目标楼层有误。");
} else { //可以服务
System.out.println( //把计算的结果进行输出
"(" + elevator.getnowfloor() + "," + sDirection + "," + elevator.getendtime() + ")");
}
}
}
}
}