交通灯管理系统的项目需求
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆---直行车辆
由西向而来去往南向的车辆--右转车辆
由东向而来去往南向的车辆--左转车辆
。。。
信号灯忽略黄灯只考虑红灯和绿灯
应考虑左转车辆受信号灯控制,右转车辆不受信号灯控制
具体信号灯控制逻辑与现实生活中普通信号灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注意:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆后放行左转车辆。
每辆车通过路口时间为一秒(可通过线程sleep的方式模拟)
随机生成车辆时间间隔以及红绿灯交替时间间隔自定,可以设置
不要求实现GUI ,只考虑系统逻辑实现,可通过log形式展现程序运行结果
需求分析
画图有助于对问题的理解和分析,所以分析问题最的办法是画图
由图分析可知
java设计是面向对象的,在在考虑问题时应该将事物以对象的思想来考虑。根据面向对象的程序设计思想,确定程序的对象有:汽车,路线交通灯,交通灯控制系统,汽车在根据自己所在的路上的红绿灯的变化确定是否穿过路口之前,先要判断前方是否有车,由路判断车的前方是否有车,路是存储车辆的集合, 路上就应该有增加车辆和减少车辆的方法。题目中不是体现车的移动过程,而是捕捉车辆穿过路口的过程,也就是路上车辆减少的过程,所以车不必设计成单独的对象,而是由字符串表示。
图中一总共有12条线路,根据实际生活中十字路口交通灯对比上图可知,向右行驶的4条线路(S2E,E2N,N2W,W2S)不受交通灯的控制,除了这四条线路,其余的8条线路中相对的两条线路由交通灯控制的通行状态是一致的,为了统一变成模型,可以假设每条线路都有一个红绿灯控制,右转弯的4条线路可以假设为常绿状态,另外其他的8条线路是两两成对的,可以归为四组,所以程序只考虑标注数字号的4条线路的控制灯的切换顺序,这四条线的反方向路线的控制灯跟随4条路线切换不必额外考虑。
程序设计
Road 类
Road类来表示路线,每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
每条路线上随机增加新的车辆,增加到一个集合中保存。
每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
public class Road {
//创建存放车辆的集合,相当于路List 接口
List<String> vehicles=new ArrayList<String>();
//构造函数
private String name;//路名
public Road(String name)
{
this.name=name;
//1-10秒钟随机种来一辆车,线程完成
//创建线程池Executors
ExecutorService pool= Executors.newSingleThreadExecutor();
//调用执行execute方法,方法的参数为runnable 的匿名内部类
pool.execute(new Runnable(){
public void run()
{
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();
}
//添加车辆
vehicles.add(Road.this.name+"_"+i);
}
}
});
//控制车辆通行的控制器
//由调度线程池完成,
ScheduledExecutorService timer= Executors.newScheduledThreadPool(1);//线程池size为1,即中有一个线程
timer.scheduleAtFixedRate(new Runnable(){
public void run()
{
if(vehicles.size()>0)//集合不为空
{
//boolean lighted=true;//灯的状态
boolean lighted=
Lamp.valueOf(Road.this.name).islighted();
if(lighted)
{
//vehicles.remove(0);//移走第一辆车(出错的代码!!!)
System.out.println(vehicles.remove(0)+"is traveling");//返回被移走的那辆车
}
}
}
},
1,//最初延迟时间
1,//期间间隔
TimeUnit.SECONDS);//单位
}
}
Lamp类
12条线路上,每一条线路上都有一个交通灯来控制,这样就有12个交通灯,无论在程序的什么地方去获得某个方向的灯时,每次获得的都是同一个实例对象,所以Lamp类用枚举来实现,永远都只有代表12个方向的灯的实例对象。
用lighted变量表示交通灯的红绿状态,绿为true ,红为false ,向右行驶的4条线路S2E,E2N,N2W,W2S不受交通灯的控制,默认为灯的状态为true ,除了这四条线路,用S2N、S2W、E2W、E2N这四个方向上的Lamp对象依次轮询变亮,Lamp对象中还要有一个oppositeLampName变量来表示它们相反方向的灯,反方向的灯的明亮状态和四个对象的状态一致,再用一个nextLampName变量来表示此灯变亮后的下一个变亮的灯。这三个变量用构造方法的形式进行赋值,因为枚举元素必须在定义之后引用,所以无法再构造方法中彼此相互引用,所以,相反方向和下一个方向的灯用字符串形式表示。
public enum Lamp {
//枚举出12个灯
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);
//枚举的构造方法都是私有的
//定义有参数的构造方法,参数传递对面的灯,下一个灯,当前灯的状态
//因为构造当前灯对面的灯可能没有创建,so传递对面灯的字符串名字
private Lamp(String opposite,String next,boolean lighted)
{
this.opposite=opposite;
this.next=next;
this.lighted=lighted;
}
//定义无参数的构造方法
private Lamp(){
}
//定义当前的灯状态
private boolean lighted;
//判断灯状态
private String opposite;//对面灯的字符串名字
private String next;
public boolean islighted()
{
return lighted;
}
//lighted 为true 时为绿灯
public void greenlight()
{
this.lighted=true;
if(opposite!=null)//有对面的灯时对面的灯为绿
{
// valueOf()返回对象
Lamp.valueOf(opposite).greenlight();
}
System.out.println(name()+"这个灯变绿下面看到六个方向汽车穿行");
}
//定义lighted为false 时是红灯
public Lamp redlight()
{
this.lighted=false;//当前灯变红
if(opposite!=null)//有对面的灯时对面的灯为红
{
// valueOf()返回对象
Lamp.valueOf(opposite).redlight();
}
Lamp nextLamp=null;//出现问题的地方!!!
if(next!=null)
{
nextLamp=Lamp.valueOf(next);
System.out.println("绿灯从"+name()+"切换为"+next);
nextLamp.greenlight();//下一个灯变为绿灯,并返回nextLamp灯
}
return nextLamp;
}
LampController类
整个系统中只能有一套交通灯控制系统,所以,LampController类最好是设计成单例。
LampController构造方法中要设定第一个为绿的灯。
LampController对象的start方法中将当前灯变绿,然后启动一个定时器,每隔10秒将当前灯变红和将下一个灯变绿。
public class LampController {
private Lamp currentLamp;
public LampController()
{
//构造函数中初始化当前的灯为由南向北的灯
currentLamp=Lamp.valueOf("S2N");
currentLamp.greenlight();
ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);//创建调度线程池
timer.scheduleAtFixedRate(new Runnable(){
public void run()
{System.out.println("aaa ");
currentLamp=currentLamp.redlight();
}
},
10,// 十秒后调度线程
10,//间隔十秒再调度
TimeUnit.SECONDS);//时间单位秒
}
检验
Main Class
设计main方法,方法中通过for循环创建12个线路的实例对象,并创建LampController 调用start 方法
public static void main(String[] args) {
String [] directions=new String[]{"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","N2E","E2N","N2W","W2S"};
for(int i=0;i<directions.length;i++)
{
new Road(directions[i]);
}
new LampController();
}
运行结果
N2S这个灯变绿下面看到六个方向汽车穿行
S2N这个灯变绿下面看到六个方向汽车穿行
E2N_1is traveling
S2N_1is traveling
N2W_1is traveling
N2S_1is traveling
E2N_2is traveling
N2S_2is traveling
W2S_1is traveling
N2W_2is traveling
绿灯从S2N切换为S2W
N2E这个灯变绿下面看到六个方向汽车穿行
S2W这个灯变绿下面看到六个方向汽车穿行
S2W_1is traveling
N2E_1is traveling
N2E_1is traveling
W2S_2is traveling