交通灯管理系统
2013-11-06 15:02 191人阅读 评论(0) 收藏 举报
--------------、、学习型技术博客、期待与您交流! --------------
交通灯管理系统
一、题目要求
模拟实现十字路口的交通灯管理系统逻辑,具体需求如:
1、异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ----直行车辆
由西向而来去往南向的车辆 ----右转车辆
由东向而来去往南向的车辆 ----左转车辆
。。。
2、信号灯忽略黄灯,只考虑红灯和绿灯。
3、应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
4、具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
5、注:
1)南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
2)每辆车通过路口时间为1秒(提示:可通过线程Sleep 的方式模拟)。
3)随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
4)不要求实现GUI ,只考虑系统逻辑实现,可通过Log 方式展现程序运行结果。
二、题意分析
1、对路口进行图解分析
分析:
1)这里总共有12条路线,每条路线作为一个对象存在。
2)为了统一编程模型,可以假设每条路线都有一个红绿灯对其进行控制。
3)其中右转弯的4条路线的控制灯可以假设称为常绿状态,而不能假设没有。
4)另外,其他的8条线路是两两成对的,可以归为4组,所以,程序只需考虑图中标注了数字的4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。
2、面向对象的分析与设计
1)对象:红绿灯,红绿灯的控制系统,汽车,路线。
a 、汽车看到自己所在路线对应的灯绿了就穿过路口吗?不是,还需要看其前面是否有车,看前面是否有车,该问哪个对象呢?该问路,路中存储着车辆的集合,显然路上就应该有增加车辆和减少车辆的方法了。
b 、再看题目,我们这里并不要体现车辆移动的过程,只是捕捉出车辆穿过路口的过程,也就是捕捉路上减少一辆车的过程,所以,这个车并不需要单独设计成为一个对象,用一个字符串表示就可以了。
c 、面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。
2)对路线这个对象的分析
每条路线上都会出现多辆车,车对象就相当于是路的数据存在,路线上要随机增加新的车,且要在灯绿期间还要每秒钟减少一辆车,路就需要有增删的方法。
a 、设计一个Road 类来表示路线,每个Road 对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road 实例对象。
b 、每条路线上随机增加新的车辆,增加到一个集合中保存。
c 、每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
3)对红绿灯和红绿灯控制系统两个对象的分析:
一个灯由绿变红时,应该将下一个方向的灯变绿。
a 、设计一个Lamp 类来表示一个交通灯,每个交通灯都维护一个状态:亮(绿)或不亮(红),每个交通灯要有变亮和变黑的方法,并且能返回自己的亮黑状态。
b、总共有12条路线,所以,系统中总共要产生12个交通灯。右拐弯的路线本来不受灯的控制,但是为了让程序采用统一的处理方式,故假设出有四个右拐弯的灯,只是这些灯为常亮状态,即永远不变黑。
c 、除了右拐弯方向的其他8条路线的灯,它们是两两成对的,可以归为4组,所以,在编程处理时,只要从这4组中各取出一个灯,对这4个灯依次轮流变亮,与这4个灯方向对应的灯则随之一同变化,因此Lamp 类中要有一个变量来记住自己相反方向的灯,在一个Lamp 对象的变亮和变黑方法中,将对应方向的灯也变亮和变黑。每个灯变黑时,都伴随着下一个灯的变亮,Lamp 类中还用一个变量来记住自己的下一个灯。
d 、无论在程序的什么地方去获得某个方向的灯时,每次获得的都是同一个实例对象,所以Lamp 类改用枚举来做显然具有很大的方便性,永远都只有代表12个方向的灯的实例对象。
e、设计一个LampController 类,它定时让当前的绿灯变红。
三、程序的编写
1、Road 类的编写:
1)每个Road 对象都有一个name 成员变量来代表方向,有一个vehicles (交通工具)成员变量来代表方向上的车辆集合。
2)在Road 对象的构造方法中启动一个线程每隔一个随机的时间向vehicles 集合中增加一辆车(用一个“路线名_id”形式的字符串进行表示)。
3)在Road 对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,是则打印车辆集合和将集合中的第一辆车移除掉。使用scheduleAtFixedRate 方法。 代码:
[java] view plaincopyprint?
1. package com.isoftstone.interview.traffic;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5. import java.util.Random;
6. import java.util.concurrent.ExecutorService;
7. import java.util.concurrent.Executors;
8. import java.util.concurrent.ScheduledExecutorService;
9. import java.util.concurrent.TimeUnit;
10.
11. /**
12. * 每个Road 对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road 实例对象。
13. * 每条路线上随机增加新的车辆,增加到一个集合中保存。
14. * 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一
辆车移除,即表示车穿过了路口。
15. * @author Godream
16. *
17. */
18. public class Road {
19. //用面向接口的方式,定义一个集合,用来存储和操作车辆这个字符串对象
20. private List vechicles=new ArrayList();
21. //定义路线名变量
22. private String name;
23.
24. public Road(String name){
25. this .name=name;
26.
27. //模拟车辆不断随机上路的过程,使用线程池,通过产生单个线程的方法,创建一个线
程池
28. ExecutorService pool=Executors.newSingleThreadExecutor();
29. //调用execute 方法,可向线程池提交一个任务,让池中的线程执行任务
30. pool.execute(new Runnable(){
31. @Override
32. //复写run 方法,需要执行的代码,随机产生车辆,并存入集合
33. public void run() {
34. for (int i = 1; i
35. try {
36. //1到10秒内随机产生一辆车
37. Thread.sleep((new Random().nextInt(10)+1)*1000);
38. } catch (InterruptedException e) {
39.
40. e.printStackTrace();
41. }
42. //车辆进入路线中
43. vechicles.add(Road.this .name+"_"+i);
44. }
45. }
46. });
47.
48. //定义一个定时器,每隔一秒检查对应的灯是否为绿,是则放行一辆车
49. ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);
50. timer.scheduleAtFixedRate(
51. new Runnable() {
52. @Override
53. //定时器要执行的代码
54. public void run() {
55. //判断该路线中是否有车,有则进行放行操作
56. if (vechicles.size()>0) {
57. //如果该路线上对应的灯是绿色的,则放行车辆
58. boolean lighted=Lamp.valueOf(Road.this .name).isL
ighted();
59. if (lighted){
60. System.out.println(vechicles.remove(0)+"\tis
traversing!");
61. }
62. }
63. }
64. },
65. 1, //隔多少秒执行
66. 1, //周期
67. TimeUnit.SECONDS/*时间单位*/);
68. }
69. }
2、Lamp 类的编写
1)系统中有12个方向上的灯,在程序的其他地方要根据灯的名称就可以获得对应的灯的实例对象,综合这些因素,将Lamp 类用java5中的枚举形式定义更为简单。
2) 每个Lamp 对象中的亮黑状态用lighted 变量表示,选用S2N 、S2W 、E2W 、E2N 这四个方向上的Lamp 对象依次轮询变亮,Lamp 对象中还要有一个oppositeLampName 变量来表示它们相反方向的灯,再用一个nextLampName 变量来表示此灯变亮后的下一个变亮的灯。这三个变量用构造方法的形式进行赋值,因为枚举元素必须在定义之后引用,所以无法再构造方法中彼此相互引用,所以,相反方向和下一个方向的灯用字符串形式表示。
3) 增加让Lamp 变亮和变黑的方法:light 和blackOut ,对于S2N 、S2W 、E2W 、E2N 这四个方向上的Lamp 对象,这两个方法内部要让相反方向的灯随之变亮和变黑,blackOut 方法还要让下一个灯变亮。
4) 除了S2N 、S2W 、E2W 、E2N 这四个方向上的Lamp 对象之外,其他方向上的Lamp 对象的nextLampName 和oppositeLampName 属性设置为null 即可,并且S2N 、S2W 、E2W 、E2N 这四个方向上的Lamp 对象的nextLampName 和oppositeLampName 属性必须设置为null ,以便防止light 和blackOut 进入死循环。
代码:
[java] view plaincopyprint? 1. package com.isoftstone.interview.traffic;
2. /**
3. * 每个Lamp 元素代表一个方向上的灯,总共有12个方向,所有总共有12个Lamp 元素。
4. * 有如下一些方向上的灯, 每两个形成一组,一组灯同时变绿或变红,所以,
5. * 程序代码只需要控制每组灯中的一个灯即可:
6. * s2n,n2s
7. * s2w,n2e
8. * e2w,w2e
9. * e2s,w2n
10. * s2e,n2w
11. * e2n,w2s
12. * 上面最后两行的灯是虚拟的,由于从南向东和从西向北、以及它们的对应方向不受红绿灯的
控制,
13. * 所以,可以假想它们总是绿灯。
14. * @author Godream
15. */
16. // S2N,S2W,E2W,E2S,N2S,N2E,W2E,W2N,S2E,E2N,N2W,W2S
17. public enum Lamp {
18. /*每个枚举元素各表示一个方向上的控制灯*/
19. S2N(false , "N2S" , "S2W" ),S2W(false , "N2E" , "E2W" ),E2W(false , "W2E" , "E2S" ),E2S
(false , "W2N" , "S2N" ),
20. /*下面元素表示与上面的元素的相反方向的灯,它们的“反方向灯”和“下一个灯”应忽略不计!
*/
21. N2S(false , null , null ),N2E(false , null , null ),W2E(false , null , null ),W2N(false
, null , null ),
22. /*下面元素表示四个右转弯方向的灯,因为其不受红绿灯控制,所以可以假设它们总是绿灯
*/
23. S2E(true , null , null ),E2N(true , null , null ),N2W(true , null , null ),W2S(true , nul
l , null );
24.
25. //当前灯的状态,是否为绿
26. private boolean lighted;
27. //当前灯变红时,下个绿的灯
28. private String next;
29. //与当前灯相反方向的同为绿的灯
30. private String opposite;
31.
32. //构造函数
33. private Lamp(boolean lighted,String opposite,String next){
34. this .lighted=lighted;
35. this .next=next;
36. this .opposite=opposite;
37. }
38. //提供一个判断是否为亮(绿)的方法
39. public boolean isLighted(){
40. return lighted;
41. }
42.
43. /**
44. * 某个灯变绿时,它对应方向的灯也要变绿
45. */
46. public void light(){
47. this .lighted=true ;
48. //为造成死循环,只将一方拥有反方向的灯
49. if (opposite!=null ) {
50. //将对应的反方向的灯变绿
51. Lamp.valueOf(opposite).light();
52. }
53. System.out.println(name()+"Lamp is green 下面将能看到六个方向的车辆通
过。 ");
54. }
55.
56. /**
57. * 某个灯变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿
58. * @return 下一个要变绿的灯
59. */
60.
61. public Lamp blackout(){
62. //当前灯变红,对应方向的灯也变红
63. this .lighted=false ;
64. if (opposite!=null ){
65. Lamp.valueOf(opposite).blackout();
66. }
67. //当前灯变红的同时,将下一个灯变绿
68. //变将下一个变绿的灯返回
69. Lamp nextLamp=null ;
70. if (next!=null ) {
71. nextLamp=Lamp.valueOf(next);
72. System.out.println(" 绿灯从" +name()+" ——>切换为" +next);
73. nextLamp.light();
74. }
75. return nextLamp;
76. }
77. }
3、LampController 类的编写
1)整个系统中只能有一套交通灯控制系统,所以,LampController 类最好是设计成单例。
2)LampController 构造方法中要设定第一个为绿的灯。
3)LampController 对象的start 方法中将当前灯变绿,然后启动一个定时器,每隔10秒将当前灯变红和将下一个灯变绿。
代码:
[java] view plaincopyprint? 1. package com.isoftstone.interview.traffic;
2.
3. import java.util.concurrent.Executors;
4. import java.util.concurrent.ScheduledExecutorService;
5. import java.util.concurrent.TimeUnit;
6.
7. /**
8. * 红绿灯控制系统用来控制红绿灯的切换时间
9. * 每隔10秒将当前灯变红,并按顺序将下一个方向的灯变绿
10. * @author Godream
11. */
12.
13. public class LampController {
14. //定义当前灯用于第一个绿的灯
15. private Lamp currentLamp;
16. public LampController(){
17. //刚开始让由南向北的灯变绿
18. currentLamp=Lamp.S2N;
19. currentLamp.light();
20.
21. //定义一个定时器,每隔10秒就将当前灯由绿变红,并将下一个灯变绿
22. ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);
23. timer.scheduleAtFixedRate(
24. new Runnable() {
25. @Override
26. public void run() { 27. //将下一个灯切换为当前的灯
28. currentLamp=currentLamp.blackout();
29. System.out.println(" 灯变了" );
30. }
31. },
32. 10,
33. 10,
34. TimeUnit.SECONDS);
35. }
36. }
4、MainClass 类的编写
1)用for 循环创建出代表12条路线的对象。
2)创建红绿灯控制系统对象,启动系统
代码:
[java] view plaincopyprint? 1. package com.isoftstone.interview.traffic;
2.
3. /**
4. * 主程序,用于启动控制系统,并实现题意过程
5. * @author Godream
6. */
7.
8. public class MainClass {
9.
10. public static void main(String[] args) {
11. /*产生12个方向的路线*/
12. String[] roads={"S2N" , "S2W" , "E2W" , "E2S" , "N2S" , "N2E" , "W2E" , "W2N" , "S2E
" , "E2N" , "N2W" , "W2S" };
13. for (int i = 0; i
14. new Road(roads[i]);
15. }
16. /* 产生整个交通灯系统并运行*/
17. new LampController();
18. }
19. }