一.思路明确
交通灯系统模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
--异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
。。。
--信号灯忽略黄灯,只考虑红灯和绿灯。
--应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
--具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
--每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
-- 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
--不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
难点:
1.如何理清思路?
2.如何建立具体对象?
3.如何想到线程问题?
一.应该对生活有很深的体验,和熟练的编程思路,此时画图法帮助很大。
总共有12条路线,为了统一编程模型,可以假设每条路线都有一个红绿灯对其进行控制,右转弯的4条路线的控制灯可以假设称为常绿状态,另外,其他的8条线路是两两成对的,可以归为4组,所以,程序只需考虑1S2N 2S2W 3E2W 4E2S 这4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。
找到突破口很重要!尽量优化条件。
二.建立具体对象
谁拥有数据,谁就对外提供操作这些数据的方法。
开始构思一下有哪些对象:路线-->车, 红绿灯--->红绿灯的控制系统
汽车看到自己所在路线对应的灯绿了就穿过路口吗?不是,还需要看前面是否有车,看前面是否有车,该问哪个对象呢?该问路,路中存储着车辆的集合,显然路上就应该有增加车辆和减少车辆的方法了。(面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。)再看题目,我们这里并不要体现车辆移动的过程,只是捕捉出车辆穿过路口的过程,也就是捕捉路上减少一辆车的过程,所以,这个车并不需要单独设计成为一个对象,用一个字符串表示就可以了。在1中初步设想的对象就减少了一个。这个项目只需要3个对象即可。
在这个十字路口,有且仅有12盏红绿灯,因此红绿灯可以用枚举来实现。其中右转的四盏灯常绿,剩下的八盏灯可以分为4组。因此只需要考虑4盏灯的红绿变化。
- 路线(Road)
- 红绿灯 (Lamp)
- 红绿灯的控制系统 (LampController)
Road 类:
public class Road
{
private List<String> vehicles = new ArrayList<String>(); //用这个List来保存这条路上的车
private String name = null; //这条路线和灯的名字
public Road(String name) //构造方法,每创建一条路时都必须为它命名
{
this.name = name;
//启动一个线程,不停地向这条路上增加车辆
ExecutorService pool = Executors.newSingleThreadExecutor(); //线程池的作用
pool.execute(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 1000; i++)
{
try
{
Thread.sleep((new Random().nextInt(10)+1) * 1000); //每1到10秒内.nextInt(10) 10以内,不包括10
} catch (InterruptedException e)
{
e.printStackTrace();
}
// vehicles.add(name + "_" + i);这样写编译会报错,因为这里访问的是这个构造方法里的局部变量。在匿名内部类里访问局部变量,这个局部变量必须用final修饰符修饰 所以这里有两种改法:1.加final修饰符,2.在这里访问外部类的成员变量。
vehicles.add(Road.this.name + "_" + i); //访问外部类的成员变量。
}
}
});
//启动一个定时器 ,这是<span style="font-family: 宋体; ">ScheduledExecutorService 里面的定时方法</span>
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate( //这个方法需要四个参数
new Runnable() //如果是绿灯,并且路上有车,就移走一辆车
{
@Override
public void run()
{
if (vehicles.size()>0)
{
boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
// if (true == lighted) 低能的表现啊。
if (lighted)
{
System.out.println(vehicles.remove(0) + "is traversing ! ");
}
}
}
},
1, //参数2:多少时间后开始执行任务
1, //参数3:每隔多少时间执行一次任务
TimeUnit.SECONDS //参数4:前面这两个时间的单位
);
}
}
Lamp类:
package com.isoftstone.interview.traffic;
public enum Lamp
{
S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false), //有业务逻辑的4个灯
N2S(null,null,false) ,N2E(null,null,false) ,W2E(null,null,false) ,W2N(null,null,false) , //和上面一一对应的4个灯
S2E(null,null,true) ,E2N(null,null,true) ,N2W(null,null,true) ,W2S(null,null,true) ; //常绿的4个灯
private boolean lighted; //用来表示灯的状态。true为绿灯,false为红灯
private String opposite;//这里要把相对应的Lamp用字符串代替,是因为S2N(N2S)这种写法可能会报错,Cannot reference a field before it is defined.
private String next; //当前灯变红时,下一盏应该变绿的灯
private Lamp(String opposite,String next,boolean lighted)
{
this.opposite = opposite;
this.next = next;
this.lighted = lighted;
}
public boolean isLighted() //返回当前灯的状态
{
return lighted;
}
public void light() // 让当前灯变绿的方法,同时检查当前灯有没有对应灯,若有,需将对应灯也变绿
{
this.lighted = true;
if (opposite != null) //这个条件判断很重要,否则就是死循环了
{
Lamp.valueOf(opposite).light();
}
System.out.println(name()+" lamp is green ,下面总共应该有6个方向能看到汽车穿过 !"); //这六个方向是指:当前路线,
//当前路线的对应路线,以及4个常绿路线。
}
// 让当前灯变红的方法,同时检查当前灯有没有对应灯,若有,需将对应灯也变红。然后,将当前灯的下一盏灯变绿。
public Lamp blackOut()
{
this.lighted = false;
if (opposite != null) //这个条件判断很重要,否则就是死循环了
{
Lamp.valueOf(opposite).blackOut();
}
Lamp nextLamp = null;
if (next != null)
{
nextLamp = Lamp.valueOf(next);
System.out.println("绿灯从 "+name()+" ---------->切换为:"+next);
nextLamp.light();
}
return nextLamp;
}
}
LampController类
public class LampController
{
private Lamp currentLamp;
public LampController()
{
currentLamp = Lamp.S2N; //最开始设定S2N这个灯是绿的
currentLamp.light();
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable() //每隔10s将当前灯变暗,同时将currentLamp变为下一盏灯。
{
@Override
public void run()
{
System.out.println("灯控定时器");
currentLamp = currentLamp.blackOut();
}
},
2,
2,
TimeUnit.SECONDS
);
}
}
MainClass类
public class MainClass
{
public static void main(String[] args)
{
//new出12条路线,再加一个灯控制器,就可以模拟一个十字路口了。
String[] directions = new String[]{
"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"
};
for (int i = 0; i < directions.length; i++)
{
new Road(directions[i]);
}
new LampController(); //红绿灯控制器
}
}
总结: