交通灯管理系统
---------- android培训、java培训、期待与您交流!----------
1、需求
模拟实现一个十字路口的交通灯管理系统,具体需求如下:(1) 异步随机生成按照各个路线行驶的车辆。
例如:
由南向而来去往北向的车辆 ----直行车辆
由西向而来去往南向的车辆 ----右转车辆
由东向而来去往南向的车辆 ----左转车辆
。。。
(2) 信号灯忽略黄灯,只考虑红灯和绿灯。
(3) 应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
(4) 具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
(5) 注意:
(a) 南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
(b) 每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
(c) 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
(d) 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
2、分析
首先根据需求画出分析图:
经过对十字路口车辆运行分析可知,每个路口共有12条线路可以经过车辆,为了统一模型,假设每条线路上都有一个红绿灯进行控制,4个右转的路线上的灯为长绿,其他8条线路上的灯是两两成对,即有4组灯,我们只需分析这几组灯的切换状态即可,如下图所示,其中G代表该方向为绿灯,R代表红灯,
3、面向对象分析
由于在Java中一切描述事物都可以看做对象,那么经过以上的分析,我们将该系统中的事物划分为不同的类,其中有车辆类、道路类,交通灯类、控制红绿灯切换的类等。
首先一个汽车类的对象询问前方路口的路是否允许通过,若路类上的灯为绿灯,则该条路允许车辆通过,否则不允许通过,但是前提是车前方没有其他车辆,这也需要向路询问,由于路上存放着车辆,而车辆保持着增加与减少车辆的状态,所以可以将路看做集合,将车看做集合中的元素,由于车辆是互不相同的,所以可以将其看做互不相同的字符串即可。
面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。
4、编写所需的类
(1) 灯类
包括四个右转弯的灯在内,一个路口共有12个灯,当然这里与现实稍有区别的是每个灯有两种颜色状态——红与绿,而现实中每条路是有三个红、黄、绿颜色的灯,这里将其简化为只有一个灯可以显示出两种颜色,由于该灯类只有12个实例对象,那么就可以应用枚举来创建这12个灯实例对象。
由于右转方向可假设为灯常绿,又相对两个方向的灯一般都是对称的,所以实际上变化的只有四个灯。编写灯类如下所示:
package com.day7;
public enum Lamp{
S2N("N2S","S2W","Green"), S2W("N2E","E2W","Red"), E2W("W2E","E2S","Red"), E2S("W2N","S2N","Red"),
N2S(null,null,"Green"), N2E(null,null,"Red"), W2E(null,null,"Red"), W2N(null,null,"Red"),
S2E(null,null,"Green"), E2N(null,null,"Green"), N2W(null,null,"Green"), W2S(null,null,"Green");
public static void main(String[] args){}
private String opposite;
private String next;
private String color;
private Lamp(String oppo, String next, String color){
this.opposite = oppo;
this.next = next;
this.color = color;
}
public String getColor(){
return this.color;
}
public Lamp switchColor(){
if("Red".equals(this.color)){
this.color = "Green";
if(opposite != null)
Enum.valueOf(Lamp.class, opposite).switchColor();
if(this.next != null){
switchNextColor(this.next);
return Enum.valueOf(Lamp.class, this.next);
}
return null;
}else{
this.color = "Red";
if(opposite != null)
Enum.valueOf(Lamp.class, opposite).switchColor();
if(this.next != null){
switchNextColor(this.next);
return Enum.valueOf(Lamp.class, this.next);
}
return null;
}
}
public void switchNextColor(String next){
Lamp temp = Enum.valueOf(Lamp.class, next);
if("Red".equals(temp.color)){
temp.color = "Green";
if(temp.opposite != null)
Enum.valueOf(Lamp.class, temp.opposite).switchColor();
}else{
temp.color = "Red";
if(temp.opposite != null)
Enum.valueOf(Lamp.class, temp.opposite).switchColor();
}
}
public Lamp nextLamp(){
return Enum.valueOf(Lamp.class, this.next);
}
}
(2) 灯控制器类
灯控制器主要利用定时器来控制灯的红绿切换。
Executors用法:
(a) 创建一个线程池
staticExecutorService newFixedThreadPool(int nThreads):
创建一个有固定线程数的线程池,传入一个整数,nThreads表示要线程池内的线程数,返回一个新创建的线程池
static ExecutorService newSingleThreadExecutor():
创建一个只有一个线程数的线程池,不用传入参数,返回一个线程池(只有一线程)
(b) 调用线程池的执行方法
调用ExecutorService(其实是一个接口)的实现类ThreadPoolExecutor的方法
public void execute(Runnable command):传入一个任务(Runnable的匿名内部类,覆写run方法),在将来某个时间执行给定任务
拓展:创建带定时器的线程池
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):
传入具有指定线程的数作为参数,创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行(理解为延迟固定时间后执行或者每个固定频率多次执行)
返回的一个具有定时器的线程池对象,再介绍该对象的方法:
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,//任务
long initialDelay,//首次执行的延迟时间
long period,//连续执行的周期
TimeUnit unit)//为上面的时间和周期指定单位,传入一枚举值
调用这个方法使得该任务每隔一个时间周期,不断执行
package com.day7;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Control {
private Lamp currentLamp;
public Control(){
this.currentLamp = Lamp.S2N;
ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
ses.scheduleAtFixedRate(
new Runnable() {
public void run() {
currentLamp.switchColor();//利用定时器每隔10s改变当前灯的颜色
}
},
10,
10,
TimeUnit.SECONDS);
}
}
(3) Road类
由以上分析可知,每个十字路口共有12个可行驶的方向,可以认为车辆可再这12条路上行驶,每个Road对象都有一个name成员变量来代表行驶路线,有一个vehicle集合来代表方向上的车辆。
在Road对象的构造方法中启动一个线程每隔一个随机的时间向vehicles集合中增加一辆车(用一个“路线名_id”形式的字符串进行表示)。
在Road对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,是则移除车辆集合的第一辆车放入到车辆行驶的方向上去。
package com.day7;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Road {
private List
Vehicle = new ArrayList
();//该集合存放该路线上的车辆
private String roadname = null;//该成员变量为该路线的名称
public Directions direction;//该变量为车辆行驶方向
public Road(String name, final Directions direction){//向有参构造方法传递参数
this.direction = direction;
this.roadname = name;
ExecutorService pool = Executors.newSingleThreadExecutor();//产生一个单线程的线程池
pool.execute(new Runnable() {
public void run() {
for(int i=1; i<100; i++){
try {
Thread.sleep((new Random().nextInt(10)+1)*1000);//随机时间1~10s内产生新车辆
} catch (InterruptedException e) {}
if(Vehicle.size() < 10)
Vehicle.add(Road.this.roadname+i);
}
}
});
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);//新建调度线程池
timer.scheduleAtFixedRate(
new Runnable(){
public void run(){
if(Vehicle.size() > 0){//判断该路线是否有车辆
if("Green".equals(Enum.valueOf(Lamp.class, Road.this.roadname).getColor())){//判断该路线的灯颜色
direction.dir.add(Vehicle.remove(0));//如果为绿灯,则移除该路上的第一辆车并存放到车辆行驶的方向
if(direction.dir.size() > 1)//刷新该方向上的车辆集合
direction.dir.remove(0);
}
}
}
},
1,
1,
TimeUnit.SECONDS);
}
}
(4) Directions类
该类用于存放车辆,例如Road类中的对象r1路线为S2N,其中的vehicle.remove(0),存放到Directions对象的集合中,用于后续的显示使用。
package com.day7;
import java.util.ArrayList;
public class Directions {
ArrayList
dir = new ArrayList
();//定义集合存放车辆
String name = null;
public Directions(String name){
this.name = name;
}
public String getVehicle(){
if(dir.size() > 0)
return dir.get(0);
else
return null;
}
}
(5) WholeSystem类
该类为主方法类,首先创建四个方向类的实例对象,然后创建12个Road类的对象,然后调用run()方法开始运行整个系统,其中还编写了函数用于显示每个交通灯的显示状态以及每个方向上的车辆增减情况。
package com.day7;
import java.util.Timer;
import java.util.TimerTask;
public class WholeSystem {
public static void main(String[] args) {
Directions d1 = new Directions("N");
Directions d2 = new Directions("W");
Directions d3 = new Directions("S");
Directions d4 = new Directions("E");
Road r1 = new Road("S2N", d1);
Road r2 = new Road("S2W", d2);
Road r3 = new Road("E2W", d2);
Road r4 = new Road("E2S", d3);
Road r5 = new Road("N2S", d3);
Road r6 = new Road("N2E", d4);
Road r7 = new Road("W2E", d4);
Road r8 = new Road("W2N", d1);
Road r9 = new Road("S2E", d4);
Road r10 = new Road("E2N",d1);
Road r11 = new Road("N2W",d2);
Road r12 = new Road("W2S",d3);
run(d1, d2, d3, d4);
}
public static void run(final Directions d1, final Directions d2,
final Directions d3, final Directions d4){
new Control();
Timer timer = new Timer();//创建定时器用于刷新显示路口状态
timer.scheduleAtFixedRate(
new TimerTask(){
public void run() {//输出每个灯的状态以及每个方向的车辆
print(Lamp.S2N.getColor().charAt(0),Lamp.S2W.getColor().charAt(0),
Lamp.E2W.getColor().charAt(0),Lamp.E2S.getColor().charAt(0),
Lamp.N2S.getColor().charAt(0),Lamp.N2E.getColor().charAt(0),
Lamp.W2E.getColor().charAt(0),Lamp.W2N.getColor().charAt(0),
d1.getVehicle(), d2.getVehicle(), d3.getVehicle(), d4.getVehicle());
}},
0,
200);
}
public static void print(char l1,char l2,char l3,char l4,
char l5,char l6,char l7,char l8,
String r1, String r2, String r3, String r4){
System.out.println(" | N | ");
System.out.println(" | | | ");
System.out.println(" | | "+r1+" | ");
System.out.println(" | | | ");
System.out.println(" | | "+l2+" "+l1+" | ");
System.out.println("----------- -----------");
System.out.println(" "+l3+" ");
System.out.println(" "+r2);
System.out.println(" "+l4+" ");
System.out.println("W---------- ----------E");
System.out.println(" "+l8+" ");
System.out.println(" "+r4);
System.out.println(" "+l7+" ");
System.out.println("----------- -----------");
System.out.println(" | "+l5+" "+l6+" | | ");
System.out.println(" | | | ");
System.out.println(" | "+r3+" | | ");
System.out.println(" | | | ");
System.out.println(" | | | ");
System.out.println(" | S | ");
System.out.println();
}
}
5、运行结果
下图为程序的运行结果,图中显示了每个交通灯的颜色状态,以及从各个方向上驶入四个方向上的车辆。
6、总结
该案例中包含的集合、多线程、定时器、枚举、内部类等等知识点,比较有综合性,并且对张老师的源代码有一些修改,增加了一些内容用于图形显示。
---------- Windows Phone 7手机开发、.Net培训、期待与您交流! ----------