张老师的项目思路实在是太清晰了,看完之后我就觉得这个项目很简单,看了一遍视频我也做出来了,张老师视频里说交通灯共有12盏,用枚举,这时我就在想,路线也是12条呀,怎么不用枚举呢?于是我用了,嘿,枚举还真好用,成功了!感觉真好!
这是一个模拟红绿灯的项目。
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
· 异步随机生成按照各个路线行驶的车辆。
· 信号灯忽略黄灯,只考虑红灯和绿灯。
· 应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
· 具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
· 每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
· 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
· 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
张老师的画图分析如下:
我写的代码:
第一个类:红绿灯类
/** * 十字路口:每个路口都有直走、左拐、右拐三条路线,四个路口就有12条,每条路线上要设置一盏交通灯。 * 右转不受灯控制,直走时相对的两条路的灯一样,左拐时相对的两条路的灯也一样,所以有4对一样灯的路, * 所以主要设置4盏灯即可,主灯亮以后让相对的灯跟着亮就行了,灭也跟着灭。 * 谁拥有数据谁叫对外提供操作这些数据的方法,所以灯应该拥有以下方法: * 1、灯拥有判断自己灯的状态的方法 * 2、把自己点亮的方法,当自己亮的时候相对的灯也要打亮 * 3、把自己熄灭的方法,当自己熄灭时候相对的灯也要熄灭,并且让下一盏灯打亮 * * 车看是否是红绿灯得问路,所以灯得跟路关联,这样路才能知道灯的状态,并告知车是否能通过,所以下一步定义路的类 * */ public enum Lamp { S2N("N2S","S2W"),S2W("N2E","E2W"),E2W("W2E","E2S"),E2S("W2N","S2N"),//这四盏是主灯,主要控制这四盏灯,通过构造函数保存了与它们相对的灯和它们的下一盏灯 N2S, N2E, W2E, W2N,//这四盏灯在路口与上面四盏一一相对,上面的亮,它就亮,上面的灭,它就灭 S2E(true),E2N(true),N2W(true),W2S(true); //这四盏是右转灯,所以通过构造函数把它们设置为绿灯 private boolean lighted; private String opposite; private String next; private Lamp() {} private Lamp(boolean lighted) {this.lighted = lighted;} private Lamp(String opposite,String next) { this.opposite = opposite; this.next = next; } public boolean isLighted() { //1、灯拥有判断自己灯的状态的方法 return this.lighted; } public void light() { //2、把自己点亮的方法,当自己亮的时候相对的灯也要打亮 this.lighted = true; if(opposite != null){ Lamp.valueOf(opposite).light(); //枚举可以通过名称获得枚举的元素 System.out.println("\n\n\n当前绿灯为:" + this.name() + "、" + opposite + "\n"); } } public Lamp blackout() { //3、把自己熄灭的方法,当自己熄灭时候相对的灯也要熄灭,并且让下一盏灯打亮 this.lighted = false; if(opposite != null) { Lamp.valueOf(opposite).blackout(); Lamp nextLamp = Lamp.valueOf(next); nextLamp.light(); return nextLamp; } return null; } }
第二个类:红绿灯控制系统
/** * 这是用来控制红绿灯的亮与灭的控制系统 * 一开始就初始化当前灯为S2N,且为绿灯,然后每隔10秒关闭当前的绿灯,并让一下盏灯为绿灯,并让这盏绿灯变成当成灯,就这样循环执行 */ import java.util.concurrent.*; public class LampController { private Lamp currentLamp; public LampController() { Lamp.S2N.light(); currentLamp = Lamp.S2N; ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);//创建一个调试线程池 timer.scheduleAtFixedRate( new Runnable() { public void run() { currentLamp = currentLamp.blackout(); } }, 10, 10, TimeUnit.SECONDS); } }第三个类:路线类
/** * 车是属于路的数据,所以路得提供操作车的方法: * 1、增加车辆,这些车辆保存到路的一个集合里,车辆增加的时间是不固定的,这里用随机 1~10秒 * 2、绿灯时让车通过,也就是减少一量增加进来的车 * 这里通过两个线程来完成上面两个方法,让路一产生的时候就进行增加车辆和检查绿灯并让车通过 * * 每条路都得有个路名,取与灯名相同方便与灯进行关联,总共有12条路 * */ import java.util.concurrent.*; import java.util.*; public enum Road { S2N,S2W,E2W,E2S, N2S,N2E,W2E,W2N, S2E,E2N,N2W,W2S; List<String> vehicles = new ArrayList<String>();//用于存放车辆 private Road() { ExecutorService pool = Executors.newSingleThreadExecutor();//产生单个线程的线程池 pool.execute(new Runnable() { public void run() { for(int i=1;i<1001;i++) { try{Thread.sleep( (new Random().nextInt(10)+1)*1000);}catch(Exception e) {e.printStackTrace();} if(name().equals("S2E")||name().equals("E2N")||name().equals("N2W")||name().equals("W2S"))//如果是右转车则把车名加上"右转" vehicles.add("-->右转 " + name() + " 第" + i + "辆车"); else vehicles.add(name() + " 第" + i + "辆车"); } } }); ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate( new Runnable() { @Override public void run() { if(Lamp.valueOf(name()).isLighted()) //如果灯绿,并且有车就删第一辆车,代表开过一辆车 if(!vehicles.isEmpty()) System.out.println(vehicles.remove(0) + " 已通过"); } }, 1, 1, TimeUnit.SECONDS); } }第四个类:运行类
public class MainClass { public static void main(String[] args) { new LampController();//把交通灯控制系统打开,红绿灯开始运转。因为灯是枚举,所以这个系统里只要一调用到任意一个灯枚举对象,则12盏灯都有了。 Road road = Road.S2N;//因为Road是枚举,所以只要一调用到Road这个类则里面的枚举元素就全部都有了,里面12条路的线程都启动了。 } }
截一断我的运行结果,如下:
当前绿灯为:S2N、N2S
-->右转 S2E 第1辆车 已通过
-->右转 W2S 第1辆车 已通过
-->右转 E2N 第1辆车 已通过
-->右转 N2W 第1辆车 已通过
N2S 第1辆车 已通过
S2N 第1辆车 已通过
-->右转 W2S 第2辆车 已通过
-->右转 S2E 第2辆车 已通过
N2E 第1辆车 已通过
S2W 第1辆车 已通过
当前绿灯为:S2W、N2E
-->右转 E2N 第2辆车 已通过
S2W 第2辆车 已通过
-->右转 S2E 第3辆车 已通过
-->右转 N2W 第2辆车 已通过
S2W 第3辆车 已通过
N2E 第2辆车 已通过
-->右转 E2N 第3辆车 已通过
-->右转 E2N 第4辆车 已通过
-->右转 W2S 第3辆车 已通过
-->右转 N2W 第3辆车 已通过
-->右转 N2W 第4辆车 已通过