题目需求:
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
1、异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
。。。
2、信号灯忽略黄灯,只考虑红灯和绿灯。
3、应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
4、具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
5、每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
6、随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
7、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
需求分析:
1、首先依据现实交通灯情形并结合题目需求分析出所有的车辆行驶情况,并画出行驶路线图,共12种行驶路线(如图所示)
2、分析12条路线在同一时刻相互之间的关系:
(1)题中指出所有右转车辆不受控制,因此可以将右转控制灯考虑为一直绿色(GREEN),因此4条右转路线不受其他路线情况的影响
(2)题中第4条又指出“南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆”
因此可以将剩余8条路线分为两组,如图所示途中所标1、2、3、4为一组,剩余a、b、c、d为一一对应的一组。
对应关系为(1-a 2-b 3-c 4-d),每一个对应关系的灯的应该是同时为红同时为绿。
这样只用控制其中的一组灯,另外一组便可一一对应被控制。可以选择按1 2 3 4顺序依次放行(即依次安排绿灯),这样另外一组便放行相对应的
3、分析该需求中需要定义哪些类?
(1)需求中出现的事物共有4种 :车、灯、路线、控制系统(控制灯的转换,隐含)
(2)分析每一种事物在需求中的出现形式:
灯:1、有属性的,红、绿,所以应该将其抽象出来。
2、又因为灯有且仅有12种灯分别控制12条路线上的车辆。可以可考虑使用枚举enum。
路线:1、路线上有车;车在受控制地减少,也在随机地增加。应该抽象出来。
2、每一个实例便是一条路线,有12条路线。(也可以考虑为enum)
控制系统:控制红绿变换的时间time,设置红绿灯的交换时间间隔的行为,控制红绿变换的行为。也应该抽象出来。
车:需求中没有车所包含的特点(属性)或者行为(方法),而车的出现仅仅是路线上的一个成员,因此不应该把车定义为对象。
综上分析,应该定义3个类,分别是 :
灯(Lamp):
定义为enum类型,因为有且仅有12个实例对象
将灯分为3组:1、由控制系统直接控制的灯,也是主要操作的灯
2、由控制系统间接控制的灯,此组灯和第一组灯相对应,因此可以隐含操作
3、右转的路线上的灯,不受控制系统控制,初始化之后不会改变
行为: 1、判断当前灯是否为绿灯。
2、当前灯变绿。变绿同时将对应的灯变绿
3、当前灯变红。变红同时将对应等变红,并把下一个灯变绿
路线(Road):
注:本人在练习时将此处的Road也定义为enum,因为它同Lamp是一个道理,有且仅有12个实例对象。
成员变量:包含该路线的名称、储存车辆的集合。
实现的行为方法:1、随机产生车辆
2、根据控制灯,移除车辆
控制系统(LampController):
定义为class,可以设置红绿灯交替的间隔时间,控制对应路线上的灯的变换。
成员变量:当前灯、控制红绿灯变换的时间
实现的行为:1、在一定频率内将当前绿灯进行切换
2、设置红绿灯切换的时间间隔
项目实现代码如下:
一、路线类Road
package com.heima.exam.traffic; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 路线 * 定义为enum,有且只有12个实例对象 * 成员变量:包含该路线的名称、储存车辆的集合。 * 实现的行为方法:1、随机产生车辆 * 2、根据控制灯,移除车辆 * @author zhangyunfei * */ public enum Road { //路线的12个实例对象 S2N("S2N"),S2W("S2W"),E2W("E2W"), E2S("E2S"),N2S("N2S"),N2E("N2E"), W2E("W2E"),W2N("W2N"),S2E("S2E"), E2N("E2N"),N2W("N2W"),W2S("W2S"); private List<String> vehicles= new LinkedList<String>(); //路线上用于存放车辆的数组, private String roadName = null; //路线的名字 private long removeTime = 1; //移除车辆的频率,按题目要求每隔1s离开一辆。单位s private Road(String roadName){ this.roadName = roadName; //初始化路线时将路线名字初始化 //定义线程,实现随机产生车辆 ExecutorService pool = Executors.newSingleThreadExecutor(); pool.execute(new Runnable(){ @Override public void run(){ for(int i = 1; i < 1000; i++){ //产生999辆车 try { Thread.sleep(((int)(Math.random()*10)+1) * 1000); //1-10秒钟随机产生车 } catch (InterruptedException e) { e.printStackTrace(); } Road.this.vehicles.add(Road.this.roadName+"_"+i); //向该路线上产生车,车以路线名_标号命名 //System.out.println("++" + Road.this.roadName+ " 方向新来了车 " + Road.this.roadName+"_"+i); // } } }); //定义定时器,实现根据控制灯移除车辆 ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate( new Runnable(){ @Override public void run(){ //根据控制系统,把该路线上的第一辆车移走 if(!vehicles.isEmpty()){//判断路线上不为空,即有车 boolean lighted = Lamp.valueOf(Road.this.roadName).isLighted();//获取当前路线的灯的颜色 if(lighted){ //如果该路线上的灯为绿色 System.out.println("--" + vehicles.remove(0) + " 开走了!");//开走路线上的第一辆车 } } } }, removeTime, removeTime, TimeUnit.SECONDS); } }
二、灯类Lamp
package com.heima.exam.traffic; /** * 灯 * 定义为enum类型,因为有且仅有12个实例对象 * 将灯分为3组:1、由控制系统直接控制的灯,也是主要操作的灯 * 2、由控制系统间接控制的灯,此组灯和第一组灯相对应,因此可以隐含操作 * 3、右转的路线上的灯,不受控制系统控制,初始化之后不会改变 * 行为:1、判断当前灯是否为绿灯。 * 2、当前灯变绿。变绿同时将对应的灯变绿 * 3、当前灯变红。变红同时将对应等变红,并把下一个灯变绿 * @author zhangyunfei * */ public enum Lamp { //控制系统主要控制的一组灯,有对应的灯、有下一个灯、初始化为红色 S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false), //第一组灯相对应的一组灯,不考虑对应灯、下一个灯,默认为红色。 N2S(null,null,false),N2E(null,null,false),W2E(null,null,false),W2N(null,null,false), //右转的路线上的灯,不受控制系统控制,一直为绿色,初始化之后不会改变 S2E(null,null,true),E2N(null,null,true),N2W(null,null,true),W2S(null,null,true); private boolean lighted; //标识灯的红绿,true为、false为红 private String opposite; //与当前灯相对的另一个灯 private String next; //当前灯的下一个灯 //参数分别为:当前灯对应的灯、下一个灯、初始化为的颜色 private Lamp(String opposite,String next,boolean current){ this.opposite = opposite; this.next = next; this.lighted = current; } //判断该灯是否为绿灯,返回true表示绿灯,返回false表示红灯 public boolean isLighted(){ return lighted; } //灯变为绿色,相对应的灯也变为绿色 public void light(){ this.lighted = true; //将对应的灯变为绿色 if(opposite != null){ //将灯分为两组,一组对面灯设置为null,防止死循环 Lamp.valueOf(opposite).light(); //由灯的名字来获取该枚举的实例对象 System.out.println(this.name() + " 灯变绿了!"); //显示路线上的灯变绿 } } //灯变为红色,并返回下一个灯的实例对象 public Lamp lightOut(){ this.lighted = false; if(opposite != null){ Lamp.valueOf(opposite).lightOut(); } Lamp nextLight = null; //将下一个灯变为绿色 if(next != null){ nextLight = Lamp.valueOf(next); System.out.println("绿灯将从 "+this.name() + " 灯切换为 "+ next); //显示路线上的灯变绿 nextLight.light(); } return nextLight; } }
三、控制系统类LampController
package com.heima.exam.traffic; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 控制系统 * 成员变量:当前灯、控制红绿灯变换的时间 * 实现的行为:1、在一定频率内将当前绿灯进行切换 * 2、设置红绿灯切换的时间间隔 * @author zhangyunfei * */ public class LampController { private Lamp currentLamp;//当前灯 private int changeTime = 5; //红绿灯交换时间间隔,默认为5s,单位s //初始化时定义当前灯,并把当前灯点亮 public LampController(){ currentLamp = Lamp.S2N; currentLamp.light(); //在设置的时间里控制灯的切换 ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate( new Runnable(){ @Override public void run(){ LampController.this.currentLamp = currentLamp.lightOut(); } }, changeTime, changeTime, TimeUnit.SECONDS); } //自行设置红绿灯交换的时间间隔 public void setChangeTime(int time){ this.changeTime = time; } }
四、包含主方法的类MainClass
package com.heima.exam.traffic; /** * 启动交通灯管理系统 * @author zhangyunfei * */ public class MainClass { /** * @param args */ public static void main(String[] args) { /* //初始化12条路线 String[] roadNames = new String[]{"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"}; for(int i = 0; i < roadNames.length; i++){ new Road(roadNames[i]); } */ //返回所有实例对象,再次只为将所有实例初始化 Road.values(); //开启控制系统 new LampController(); } }