结对编程项目——电梯调度
电梯调度
现有一新建办公大厦,共有21层,共有四部电梯,所有电梯基本参数如下表所示:
电梯编号 |
可服务楼层 |
最大乘客数量 |
最大载重量 |
1 |
全部楼层 |
10 |
800 kg |
2 |
单层 |
10 |
800 kg |
3 |
双层 |
20 |
1600 kg |
4 |
全部楼层 |
20 |
2000 kg |
其使用规定如下:
1、楼层号为0~20,其中0号为地下一层;
2、有楼层限制的电梯不在响应楼层停靠,如单双层;
3、所有电梯采用统一按钮控制
请根据上述要求设计并实现一个电梯控制程序,如果有图形显示就更好了。
伙伴介绍与评价:
本次结对编程项目(电梯调度)我的伙伴是刘承颖,这是他的博客链接http://www.cnblogs.com/tiezhu 。
经过两人的协商,最终决定分工为:我主编程,他主审查。以下为我们工作中的一些片段:
虽然我的结对伙伴基础有些薄弱(其实我也不行),但在这次的结对编程项目合作过程当中,我还是明显的感觉到,首先对方的能力是值得肯定的,工作态度很认真,有耐心,悟性也高,同时也经常能提出一些具有建设性的意见。再则我要承认对方是比我细心的(我眼神真的不行),经常能够指出一些错误(我经常切到中文输入法,导致中文字符乱入而编译报错)。最后也要说一下对方的不足之处,由于这次我使用的是java语言,对方之前没有接触过,所以交流的过程中也遇到了许多障碍。
需求分析与简单构思:
所谓电梯调度,我认为就是在有限的条件下,尽最大努力减少乘客的等待时间,同时让更多乘客能坐到电梯。不过如果细想的话,要考虑的事情还是非常的多的(一开始看到“如果有图形显示就更好了”这句话的时候我整个人都傻了)。
1.四部电梯,且这四部电梯的信息各不相同(载客数,载重能力)。更为特殊一点的是2、3号这种有楼层限制的电梯,提到楼层限制,我就想到了我们学校图书馆的电梯(1-6楼不停),应该还是单双层更难一些。这里初步构想用以编号不同(1-4)来区别这些信息,感觉这应该没什么争议。
2.调度问题:一共21层楼,0代表地下一层。首先,要考虑一下初始状态,没什么问题的话感觉要么是一开始4部电梯都停在最底层(即地下一层)要么是一开始都停在大堂(即1楼,毕竟1楼人经常是最多的)。后来我又结合这4部电梯的特殊性,想到一个最优的初始位置即:1号电梯最初停在1楼(最普通的电梯),2号电梯最初停在7楼(单层电梯),3号电梯最初停在14楼(双层电梯),4号电梯停在0楼(地下一楼,承载能力最强,适合运送货物,这里我猜大重量的货物应该在地下室。。。) 虽然我感觉这种安排1 、7、14、0的安排不仅发挥的各自电梯的优势而且能够保证一开始的调度时间最短。但是后来想想,现实中好像没有这么安排的。然后重要的便是电梯调度算法,关于在这个电梯调度算法,我想了很久,而且在变成过程中也不断在改变自己的想法,因为我觉得实在是在复杂了,有些问题更是完全矛盾的,难以取舍的。比如1号电梯在3楼处于空闲状态,2号电梯正载着人从底层向上最终要去7楼,当2号电梯刚到上升到3楼的时候,5楼有人按了↑的按钮。那么此时是让空闲的1号电梯去呢?还是顺路且刚好仅为单层停靠的2号电梯去呢?等等此种让人纠结的问题数不胜数。最后我在无限的纠结中只能放弃,或许真的是我想太多了。应该从简单些入手。设触发外部按键的楼层为请求楼层,触发内部楼层按键为目的楼层,电梯目前所处楼层为当前楼层。这里我觉得一个最基本的算法便是要让(请求楼层-当前楼层)最小的去响应外部按键,同样优先让(目的楼层-当前楼层)最小的去响应内部按键(不同的人有不同的目的楼层)。
3.界面问题:这个真的好难啊,我以前从来没做过任何界面,电梯是动的,如果用让图形“动起来”是最大的问题。最后我实在是不理解“所有电梯均采用统一按钮控制”这句话是什么意思,一个按钮怎么控制4个电梯的内部请求,21个楼层的外部请求。
实现过程:
一开始进行的还是比较顺利的,各种class先弄出来。但是后来解决不了的问题来了...下面附上一小段最初的失败作品的代码:
class ElevatorInfo { int Elevator_Num; int Elevator_Service; int Elevator_MaxPassenger; int Elevator_MaxWeight; public ElevatorInfo(int a,int b,int c,int d) { this.Elevator_Num=a; this.Elevator_Service=b;//可服务楼层:0代表全部,1代表单层,2代表双层。 this.Elevator_MaxPassenger=c; this.Elevator_MaxWeight=d; } } class ElevatorStatus { int Num; int CurrentPassengers; int CurrentWeight; int CurrentFloor; int Status; //电梯运行状态:0代表停止,1代表向上,-1代表向下 int Arrive; //电梯是否到达目标楼层:1代表到达,0代表未到达 public ElevatorStatus(int n) { switch(n) { case 1:this.CurrentFloor=1;break; case 2:this.CurrentFloor=7;break; case 3:this.CurrentFloor=14;break; case 4:this.CurrentFloor=0;break; } this.Num=n; this.CurrentPassengers=0; this.CurrentWeight=0; this.Status=0; this.Arrive=0; } } class RequestInfo { int RequestFloor; int UpOrDown; int Passengers; int Weight; }
或许是由于我刚开始学Java,水平实在是太菜了,跟着http://www.cnblogs.com/vamei/archive/2013/03/31/2991531.html这个网站上的教程胡乱写,越写越像C语言,后来研究好久,不用多线程根本不能让4个电梯同时运作,然而我并不会,去网上百度一下,也没怎么弄明白,而且界面部分也不熟练。实在是写不出来,最终这个“初稿”也就加起来200行左右,没有什么明显Java的样子,只能实现控制台里的输入一些乘客、重量、外部请求楼层什么的然后输出一个调用的电梯信息。后来经过我坚持不懈的各种百度,查书。“借鉴”了各种电梯程序中多线程和界面的部分。经过两三天的增删改查终于算是完成了。但是问题还是很大的,多线程还是弄不明白,没有实现电梯单双层问题,电梯之间也没有乘客数量和载重能力的区分(这里我感觉可能需要每个线程都不一样,好麻烦,只好妥协了)不过我觉得已经无所谓了,这次项目真的让我学到很多。所以重点在总结里。
总结和体会:
1.Java十分明显的面向对象的特点。
2.程序稍微庞大一些后,其中需要需要定义的变量名很多,要仔细设计一下他们的名称,否则经常容易混淆。最好都翻译成英文。
3.在做界面时,各种常见的如Frame、Panel、Button、TextFiled等等。
4.创建一个新的窗口后,要记住加上setVisible(true)来让其显示。相反可用setVisible(false)让其隐藏。
5.setBounds与setLayout,前者既然定位又定义大小setBounds(int x,int y,int width,int height),后者定义布局方式,常用setLayout(null)和setLayout(new FlowLayout())
6.事件监听机制以及常见的鼠标事件、键盘事件。下面附上一段程序中的代码,其中包含了窗口框架,文本框,按钮,鼠标事件实现弹窗。
class PassengerAndWeightAction implements ActionListener { //输入人数和重量 private JFrame f1; private JFrame f2; private TextField tf1; private TextField tf2; private TextField tf3; private Button but; public void actionPerformed(ActionEvent e) { f1 = new JFrame("请输入乘客数和重量"); f2 = new JFrame("错误"); f1.setLayout(new FlowLayout()); f1.setBounds(450,350,400,100); f1.setVisible(true); f1.set f2.setBounds(450,350,260,70); tf1 = new TextField(10); tf2 = new TextField(10); tf3 = new TextField(16); tf3.setText(" 人数或重量超过限制,请返回重新输入!"); but = new Button("确认"); f1.add(but); f1.add(tf1); f1.add(tf2); f2.add(tf3); but.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e){ int p = Integer.parseInt(tf1.getText()); int w = Integer.parseInt(tf2.getText()); if(p>20||w>2000) { f2.setVisible(true); } else { f1.dispose(); } } }); } }
7.在elipse中记得运用Ctrl+Shift+O来一键import,如各种java.awt以及javax.swing
8.在一些鼠标与键盘事件中由于传递的参数为String类型,如果想想要获取文本框中的int类型信息,可采用Integer.parseInt(TextField.getText())语句,例如:
but.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e){ int p = Integer.parseInt(tf1.getText()); int w = Integer.parseInt(tf2.getText()); if(p>20||w>2000) { f2.setVisible(true); } else { f1.dispose(); } } });
9.Java中封装和接口,public,private区别。 implements用法。
10. Java中的继承extends,以及接口的继承。虽然不支持多重继承,但可以用接口来实现。
class UpAction implements ActionListener { private int floor; public UpAction(int i) { floor = i; } public void actionPerformed(ActionEvent e) { bUp[floor].setForeground(Color.RED); bUp[floor].setEnabled(false); foundThread.addTask(floor, true); } } class DownAction implements ActionListener { private int floor; public DownAction(int i) { floor = i; } public void actionPerformed(ActionEvent e) { bDown[floor].setForeground(Color.RED); bDown[floor].setEnabled(false); foundThread.addTask(floor, false); } }
11.最常见的:构造器与方法重载、组合、类数据与类方法。
class Elevator extends Thread { //电梯运行主类 private int id; private int floor = FLOOR; private JPanel myPanel = new JPanel(); private JButton displaystate = new JButton("-"); private JButton[] buttons; public int currentfloor = 0; public int currentstate = PAUSE; private JButton displayfloor = new JButton("0"); private ArrayList destinations; public Elevator(int x) public void addFloor(int i) public void wantToFloor(int i)
public void setState() }
12.如果定义的变量名混淆了,需要更改,在eclipse中可以选中需要更改的变量名按Alt+Shift+R来更改程序中的所有的该变量名。
13.最常犯的错误便是中文输入法乱入问题,要留意 ;和 ; ()和 ()、“” 和 "" 等等符号。切记编程一定要细心。
本次结对编程收获颇丰,首先要感谢一下我的结对伙伴的耐心与细心的审查,帮我指出了需要许多错误与改进的的地方。我觉得在整个过程中,我们的程序的一步一步的改善,直到最终成型,离不开彼此之间的交流讨论,同时也让双方都学到了很多新的知识。我们在本次结对编程项目中的合作十分愉快。虽然最后的成品没有完全实现所有要求,但是这次项目重要的是让一个初学Java的我巩固了很多基础知识的同时也学到了很多新的知识,相信这次项目对我的Java学习有着极大的帮助。更重要的是让我更进一步了解了结对编程,实实在在的体验了一次结对编程,学会了如何更好的与他人合作来完成一个项目。