项目需求:
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
信号灯忽略黄灯,只考虑红灯和绿灯。
应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
程序最后结果演示:
现实模拟:
如图所示:一个十字路口分,东,南,西,北四个方向,每个方向又分左行道与右行道,每条行道上又分出,左转道,右转道,直行道。
依据国内的交通法则,行人与车辆应该靠右行驶。在任何时候相对于车辆的前进方向来说都应该在靠右行驶。
排除掉已经通过路口的车辆不算,那么还剩下东,南,西,北四个方向上的右行道车辆,也就是12条道。
因为右转向车并不会与其它车产生行道交错,故而右转向车并不受交通灯的影响,剩下来的就只剩8条道,也就是说需要控制8条道上的车,也就是8个灯。如图中蓝色箭向的方向。
南向北,北向南来往的车并不会行道交错,所以南向北,北向南灯可以保持一致。同理,东西向也是如此。
南向西,北向东行驶的车也不会产生彼此交错影响,所以交通灯也可以保持一致,同理,东向南与西向北也是如此。
经以上分析得出,我们需要控制8个交通灯,而这8个灯中有一半是一致的,换句话说我们只需要4个灯。
项目分析:
1.12个灯对应12条道路,12条道路对应12个对象,每条道路对象应该知道当前自己所拥有的车辆和自己的灯,根据谁拥有数据谁就提供数据的操作方法这一面向对象设计推理,那么道路对象还应该提供出一个车辆减少方法和增加方法。
2.车辆的减少与增加行为处于两个平行线互不影响,所以需要两个独立的线程来完成这一工作,可以将道路提供的增加车辆与减少车辆方法封装进线程run方法中。车辆的储存使用一个集合列队,该集合需要同步化。因为每一个道路对象都拥有自己的车辆队列集合,增加车辆方法和减少车辆方法,所以每一个道路对象自身都有两个独立的线程在操作同一个集合。
3.增加车辆方法由随机数决定向集合末尾增加新车辆的时间,减少车辆的方法需要调用当前道路对应的灯的状态决定是否减车。
4.使用枚举表示不同道路上的灯,因为需要数据统一化实现多态性的方法调用,所以12条道产生12个灯,4个右转向灯为长亮灯。其余8个灯其中有一半互不影响所以可以将互不影响的两个灯绑定在一起。
5.灯本身应该提供转绿与转红的方法即开关方法,在一个灯开或关的同时应该影响到与它绑定的那个灯,关闭时需要通知下一个灯亮起。在这个过程中可能会涉及到一个死循环或空指针问题,所以必须判断当前灯是不是有被绑定的灯,是不是有下一个灯。
6使用一个独立的线程,从一个起始亮起的灯开始,调用它的关闭方法。它的关闭方法会同时关闭与之绑定的灯,同时通知它的下一个灯亮起。由于12个灯中4个右转灯不用管,剩余8个又有一半反方向灯时间与另一半相同,所以程序只需要控制4个灯的循环亮起即可。
代码实例:
Lamp枚举
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);
Lamp(String opposite, String next) {
this.opposite = opposite;
this.next = next;
}
Lamp(boolean lighted) {
this.lighted = lighted;
}
Lamp(){}
//与当前灯相反方向灯
private String opposite = null;
//当前灯变红时下一个变绿的灯
private String next = null;
//当前灯状态
private boolean lighted = false;
//判断当前灯的状态
public boolean isLighted() {
return lighted;
}
//打开当前灯与相反方向的灯
public void light() {
lighted = true;
if(opposite != null) {
Lamp.valueOf(opposite).light();
System.err.println(this + "," + Lamp.valueOf(opposite) + ":路灯已转为[绿灯]!");
}
}
//关闭当前灯与相反方向的灯
public Lamp blackOut() {
lighted = false;
if(opposite != null) {
Lamp.valueOf(opposite).blackOut();
System.err.println(this + "," + Lamp.valueOf(opposite) + ":路灯已转为[红灯]!");
}
//如果有下一个灯那么通知它开启
Lamp nextLamp= null;
if(next != null){
nextLamp = Lamp.valueOf(next);
nextLamp.light();
}
return nextLamp;
}
//用于打印输出
@Override
public String toString() {
String str = "从" + eToC(name().charAt(0)) + "到" + eToC(name().charAt(2));
if (this.ordinal() >= S2E.ordinal()) {
str += "走<<右行道>>不用看路灯很快";
}else if(this.ordinal() % 2 == 0){
str += "<<直行道>>";
}
else {
str += "走<<左行道>>请注意安全";
}
return str;
}
//用于转换字符
private String eToC(char e) {
String str = null;
switch (e) {
case 'E':
str = "东";
break;
case 'S':
str = "南";
break;
case 'W':
str = "西";
break;
case 'N':
str = "北";
break;
}
return str;
}
}
LampController灯控制器
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class LampController {
//当前灯
private Lamp currentLamp = Lamp.S2N;
public LampController(int durationTime) {
//将第一个灯设置为红灯
currentLamp.light();
//创建一个定时线程池
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
//每隔durationTime秒触发一次线程
timer.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
//调用当前灯的关闭方法,并记录返回的下一个灯
currentLamp = currentLamp.blackOut();
}
},
durationTime,
durationTime,
TimeUnit.SECONDS);
}
}
Road道路
import java.util.*;
import java.util.concurrent.*;
public class Road {
//车辆集合
private List<String> vechicles = Collections.synchronizedList(new ArrayList<String>());
//道路名
private String name = null;
public Road(String name) {
this.name = name;
//开启新的线程池用于模拟产生车辆
ExecutorService pool = Executors.newSingleThreadExecutor();
//启动新线程
pool.execute(new Runnable(){
@Override
public void run() {
//循环产生车辆
String str = null;
for (int i = 1; i < 1000; i++) {
try {
//每产生一辆车随机休息1到10秒
Thread.sleep((new Random().nextInt(10) + 1) * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
str = Lamp.valueOf(Road.this.name) + "---第" + i + "辆车";
//将新产生的车辆加入到集合中
vechicles.add(str);
}
}
});
//开启新的线程池用于查看当前灯状态放行车辆
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
//建立线程触发器
timer.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
//判断当前道路上是否有车
if (vechicles.size() > 0) {
//调用枚举的静态方法,以枚举的字符串表现形式做为参数获取该枚举对象
if (Lamp.valueOf(Road.this.name).isLighted()) {
//如果是绿灯那么通过一辆车
System.out.println(vechicles.remove(0) + "通过路口!");
}
}
}
},
1, //1时间后启动触发器
1, //每隔1时间触发一次
TimeUnit.SECONDS);//触发器单位时间量为秒
}
}
Main函数入口
public class MainClass {
public static void main(String[] args) {
//使用数组转载12个灯,便于后续操作。
String[] directions = new String[]{
"S2N","S2W","E2W","E2S",
"N2S","N2E","W2E","W2N",
"S2E","E2N","N2W","W2S"};
//循环创建12个道路对象,并将对应的灯传递进去
for(int i=0;i<directions.length;i++){
new Road(directions[i]);
}
//参数用于指定灯转换的时间间隔
new LampController(10);
}
}