项目要求
交通灯管理系统
模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:
异步随机生成按照各个路线行驶的车辆
例如:
由南向而来去往北向的车辆 ---- 直行车辆
由西向而来去往南向的车辆 ---- 右转车辆
由东向而来去往南向的车辆 ---- 左转车辆
。。。
信号灯忽略黄灯,只考虑红灯和绿灯
应考虑左转车辆控制信号灯,右转车辆不受信号灯控制
具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆
每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)
随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果
分析
面向对象的分析和设计,有哪些对象?
路线
车辆
灯(红绿黄)
控制灯切换的控制器,也是一个对象
路线共有12条,如图中箭头所示
没有必要把车辆设计成对象,因为只是捕捉车辆经过的过程,用一个字符串表示车辆就可以了
车辆看到自己所在路线对应绿灯就穿过马路吗?不是,还要看前面是否有车,该问哪个对象前面是否有车呢?该问路,因为路拥有汽车,所以汽车的增加减少方法都在路对象上
无论何时只有十二个灯,所以十二盏灯使用枚举表示。右拐弯的路线本来不受灯的控制,但是为了让程序采用统一的处理方式,故假设有四个右拐弯长绿灯
灯控制器控制车辆的放行顺序:1-2-3-4(如上图),往复循环
Road类
import java.util.*;
import java.util.concurrent.*;
/**
路拥有车辆,所以路负责产生车辆、减少车辆。即向集合中加入元素,车辆经过表示为从集合中移除元素
*/
class Road
{
private String name; //路的名称
//存放车辆的集合
List<String> vehicles = new ArrayList<String>();
public Road(String name)
{
this.name = name;
//开启一个线程池,里面的一个线程负责产生车辆
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Runnable(){
public void run()
{
for(int i = 1; i < 1000; i++)
{
try
{
Thread.sleep((new Random().nextInt(10) + 1) * 1000);
}
catch(InterruptedException e)
{
}
vehicles.add(Road.this.name + "_" + i);
}
}
});
//开启一个计时器线程池,满足绿灯且有车的情况下,每一秒过一辆车
ScheduledExecutorService pool2 = Executors.newScheduledThreadPool(1);
pool2.scheduleAtFixedRate(
new Runnable()
{
public void run()
{
//满足是绿灯且有车的情况下才行驶车辆
if(Lamp.valueOf(Road.this.name).isGreenLighted())
{
if(vehicles.size() > 0)
{
String vehicle = vehicles.remove(0);
System.out.println("车辆 " + vehicle + "路过");
}
}
}
},
1,
1,
TimeUnit.SECONDS);
}
}
Lamp类
/**
假设一个方向一个灯,一个灯只有红绿两种状态(不考虑黄灯),总共12盏灯。其中右拐的4个方向假设各有一个常绿的灯
为什么把灯设计成枚举?
因为灯的数量是固定不变的
*/
enum Lamp
{
/*
定义12盏灯,为什么传入的是字符串而不是Lamp类型的灯?
以S2N("N2S", "S2W", false)为例,如果直接传入Lamp类型的N2S,会报错误,因为此时还不存在N2S对象
*/
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);
//boolean类型表示灯的颜色,绿true,红false
private boolean isGreen = false;
//相对应的灯,如果没有则为null
String opposite = null;
//下一个灯,如果没有则为null
String nextLamp = null;
private Lamp(String opposite, String nextLamp, boolean isGreen)
{
this.opposite = opposite;
this.nextLamp = nextLamp;
this.isGreen = isGreen;
}
public boolean isGreenLighted()
{
return isGreen;
}
//灯变绿的方法,对应的灯也要变绿
public void turnGreen()
{
this.isGreen = true;
//name()是枚举中的方法,返回枚举对象名称
System.out.println(name() + "变绿灯,下面总共应该有6个方向能看到汽车穿过");
if(opposite != null)
{
Lamp.valueOf(opposite).turnGreen();
}
}
//灯变红的方法,对应的灯也要变红,如果存在下一个灯,则下一个灯变绿,返回下一个灯
public Lamp turnRed()
{
this.isGreen = false;
System.out.println(name() + "变红灯,禁止通行");
if(opposite != null)
{
Lamp.valueOf(opposite).turnRed();
}
/*为什么要新定义一个Lamp next?(我遇到了下面的问题)
如果直接使用nextLamp,代码如下:
if(nextLamp != null)
{
Lamp.valueOf(nextLamp).turnGreen();
}
return Lamp.valueOf(nextLamp);
如果这里的nextLamp等于null,即return Lamp.valueOf(null); //存在问题
*/
Lamp next = null;
if(nextLamp != null)
{
next = Lamp.valueOf(nextLamp);
next.turnGreen();
}
return next;
}
}
LampControl类
import java.util.concurrent.*;
/**
灯控制器,控制灯每隔10秒切换一次
*/
class LampControl
{
private Lamp currentLamp;
public LampControl()
{
//设置当前灯为S2N
currentLamp = Lamp.S2N;
//当前灯变绿
currentLamp.turnGreen();
//开启一个计时器线程池,每10秒把当前灯变红,并设置下一个灯为绿灯
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.scheduleAtFixedRate(
new Runnable()
{
public void run()
{
currentLamp = currentLamp.turnRed();
}
},
10,
10,
TimeUnit.SECONDS);
}
}
主程序
/**
产生12条路,开启灯控制器
*/
class TrafficLampTest
{
public static void main(String[] args)
{
//12条路的名称
String[] roadsName = {"S2N", "S2W", "E2W", "E2S", "N2S", "N2E", "W2E", "W2N", "S2E", "E2N", "N2W", "W2S"};
//产生12条路
for(int i = 0; i < roadsName.length; i++)
{
new Road(roadsName[i]);
}
//开启灯控制器
new LampControl();
}
}
---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------