---------------------- <a href="http://edu.youkuaiyun.com"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.youkuaiyun.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
一、题目要求:
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
1.异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
。。。
2.信号灯忽略黄灯,只考虑红灯和绿灯。
3.应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
4.具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
5.每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
6.随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
7.不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
二、项目需求分析与设计
1.首先由于题目给出的十字路口交通路线比较多,画出十字路口道路模拟图:
2.需求分析
由上图可以看出,箭头所指的路线一共有12条,那么应该有12条道路。根据现实生活中的交通规则,所有的向右拐的路线是不需要看信号灯的。那么还剩下8条道路,这8条道路是两两相对的,归为4组。比如:从南向北的道路信号灯如果是亮起(绿灯)的话,那么相对应的从北向南的道路信号灯也一定是绿灯。可以从每组中各抽取一条线路,我们切换这4条道路信号灯,那么相对应的那4条道路信号灯也就可以跟着切换。
3.设计本项目中需要的类
根据需求,我们发现有信号灯、道路、车辆、信号灯控制器等对象。信号灯控制器控制信号灯,信号灯控制道路上的车辆。
经过细心考察,我们发现车辆不需要单独创建成一个类。因为车辆的产生与通过道路不需要在程序中体现出来,只需要在通过时,给一个提示即可。我们可以让道路产生两个方法,随机产生车辆和移动车辆过马路,这样可以是程序变得更简单明了。
信号灯类又有什么方法呢?首先,一共有12条道路,每条道路都对应一个信号灯,那么就有12个信号灯。那我们可以用枚举来描述信号灯类,除去向右转的4条道路可以一直当做信号灯常亮状态,其余每条道路上的信号灯都有亮起(绿灯)和熄灭(红灯)状态。
信号灯控制器类可以控制信号灯亮起状态的时间,并每隔一段时间进行切换。
三、项目编程
道路类:Road
具体步骤:
1.定义一个集合存储每条道路上的车辆。
2.因为要有12条道路,可以在构造方法中创建一个线程,每隔一段时间,就会随机产生一个车辆。
具体可以用线程池Executor类来实现。
3.定义一个计时器,每隔一段时间检测信号灯是否亮起,如果亮起,每隔一秒使一辆车通过道路。
package com.isoftstone.interview.traffic;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 道路类
* @author 中关村阿旺
*
*/
public class Road {
//面向接口编程,list集合里面存放的是每条道路随机产生的车辆
//因为用不到车辆的方法,所以不用创建车辆类,只用一个字符串代表一辆车即可
private List<String> vehicles=new ArrayList<String>();
//道路的名称
private String name=null;
public Road(String name){
this.name=name;
//Executors类可以创建一个线程池,也就是一组线程。
//static ExecutorService newSingleThreadExecutor()
//创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
ExecutorService pool = Executors.newSingleThreadExecutor();
//void execute(Runnable command)
//在未来某个时间执行给定的命令。 pool线程池会在自身内部挑选某个线程执行给定的命令。
//返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。
pool.execute(new Runnable() {
public void run() {
//假设每条道路上产生1000辆车
for (int i = 1; i < 1001; i++) {
try {
//static void sleep(long millis)
//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
//Random 此类的实例用于生成伪随机数流。随机在1~10秒内创建一辆车。
//int nextInt(int n)
Thread.sleep((new Random().nextInt(10)+1)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
vehicles.add(Road.this.name+"_"+i);
}
}
});
//static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
//创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 也就是创建了一个定时器,每隔多久检测一下交通灯是否变绿。
ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);
//ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
//创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
//也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。
timer.scheduleAtFixedRate(
new Runnable(){
public void run(){
//移除道路上的车
if(vehicles.size()>0){
//根据本条道路的名称得到本条道路的交通灯枚举对象,然后调用灯的状态
boolean flighted=Lamp.valueOf(Road.this.name).isLighted();
//如果交通灯是绿的
if(flighted){
//移除排在第一位的车辆,并打印信息。
System.out.println(vehicles.remove(0)+" is traversing!");
}
}
}
},
1,
1,
TimeUnit.SECONDS);//TimeUnit 表示给定单元粒度的时间段,它提供在这些单元中进行跨单元转换和执行计时及延迟操作的实用工具方法。
}
}
交通灯类:Lamp(枚举)
具体步骤:
1.12条道路对应12个交通灯枚举对象,首先创建12个枚举对象。
2.S2N、S2W、E2W、E2S这4个对象都有对应的对象,在它们处于亮起状态时,它们对应的对象也要处于相同的状态。
其它4个对象一直处于亮起状态。当进行切换时,还要知道下一个信号灯对象是哪个,也就是让那个信号灯处于亮起状态。
可以在构造函数中传递3个参数,一个表示相对应的灯,一个表示自己的状态,一个表示下个信号灯对象。
3.信号灯有两个状态,亮起和熄灭。可以在这两个方法中使对应的信号灯与自己的状态保持一致,并且当处于熄灭状态时,要将下个信号灯的状态改为亮起。
package com.isoftstone.interview.traffic;
/**
* 枚举,交通灯
* @author 中关村阿旺
*
*/
public enum Lamp {
//注:S代表South,南方;N代表North,北方;E代表East,东方;W代表West,西方。2取自谐音to。例:S2N代表从南往北行驶的道路。
//下面这十二个枚举对象代表着十二条线路。
S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false),
//为防止出现方法的调用死循环,所以传入null值。
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);
/**
* 带参的构造方法
* @param opposite 相对应的灯的名称
* @param nextLamp 下一个该亮起的灯的名称
* @param lighted 自己所处于的状态
*/
private Lamp(String opposite,String nextLamp,boolean lighted){
this.opposite=opposite;
this.nextLamp=nextLamp;
this.lighted=lighted;
}
//描述交通灯是否处于亮起状态
private boolean lighted;
//对应的交通灯(比如:南方的灯亮起,那么相对的北方的灯也要亮起,相对的灯状态要保持一致)
//为什么要用String类型呢?因为我们的枚举类初始化时,就要有相对应的灯
//但是,第一个灯初始化时,对应的灯还没有初始化呢,这时程序会报错。所以先用String类型描述一下,用的时候再转换成枚举对象。
private String opposite;
//下一个交通灯。此交通灯熄灭之后,用来描述下一个要亮起的交通灯。
private String nextLamp;
//得到此交通灯的状态
public boolean isLighted(){
return this.lighted;
}
//使此交通灯处于亮起状态
public void light(){
this.lighted=true;
//因为两条相反方向的道路对应的灯如果来回调用彼此,会形成死循环。所以要判断一下。
if(opposite !=null){
//使相对应的交通灯也处于相同的亮起状态
Lamp.valueOf(opposite).light();
}
System.out.println(name()+"灯变绿,下面应该总共有6个方向可以看到车辆通过。");
}
//使此交通灯处于熄灭状态
public Lamp blackOut(){
this.lighted=false;
if(opposite !=null){
//使相对应的交通灯也处于熄灭状态
Lamp.valueOf(opposite).blackOut();
}
Lamp next =null;
//由于本交通灯处于了熄灭状态,那么使下一个灯处于亮起状态
if(nextLamp !=null){
next=Lamp.valueOf(nextLamp);
System.out.println(this.name()+"灯熄灭,"+next.name()+"灯变绿。");
next.light();
}
return next;
}
}
交通灯的控制器类: LampController
具体步骤:
1.在构造方法中自定义让某个信号灯处于亮起状态,以便定时切换。
2.定义一个定时控制器,每隔一段时间切换信号灯的状态,并让下一个信号灯亮起。
package com.isoftstone.interview.traffic;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 交通灯的控制器类
* @author 中关村阿旺
*
*/
public class LampController {
//当前交通灯
private Lamp currentLamp;
public LampController(){
//随意选择一条道路上的灯作为当前交通灯
currentLamp=Lamp.S2N;
//使此交通灯处于亮起状态
currentLamp.light();
//创建一个定时器,每隔10秒使当前交通灯的状态改变,也就是由亮起变为熄灭状态。
ScheduledExecutorService timer= Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){
public void run() {
//使当前交通灯处于 熄灭状态,并返回下一个交通灯对象
currentLamp=currentLamp.blackOut();
}
},
10,
10,
TimeUnit.SECONDS);
}
}
测试类:MainClass
具体步骤:
在此类中创建12个道路对象,并启动交通灯系统。
package com.isoftstone.interview.traffic;
/**
* 交通灯系统的测试类
* @author 中关村阿旺
*
*/
public class MainClass {
public static void main(String[] args) {
//因为有十二条道路,所以系统中要产生十二个Road对象,可以把道路名称存放到一个数组中,遍历数组创建对象。
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();
}
}
---------------------- <a href="http://edu.youkuaiyun.com"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.youkuaiyun.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://edu.youkuaiyun.com" target="blank">http://edu.youkuaiyun.com</a>