import java.util.*; //导入工具包
import java.util.concurrent.*; //导入工具子包并发包
/**
* 产生多个方向的路线和整个交通灯系统
* @author 张孝祥-黑马程序员
* @editor 魏安-黑马程序员
*/
public class Traffic //交通灯类
{
public static void main(String[] Ann) //主函数
{
String[] directions = {"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"}; //定义字符串数组为方向路线
int index = directions.length - 1; //定义数组角标
Lamp lamp = Lamp.valueOf(directions[index]); //定义枚举值为数组对应角标值
boolean flag = ( lamp == Lamp.S2E || lamp == Lamp.N2W || lamp == Lamp.E2N || lamp == Lamp.W2S ); //定义判断标记为虚拟绿灯状态
if ( ! flag ) //如果不符合标记条件
{
for ( int i = 0; i < directions.length; i++ ) //对整个数组遍历
{
new Road( directions[i] ); //建立道路类对象并传入数组对应角标参数值
}
new LampController(lamp); //建立灯控制器对象
}
else //否则执行
{
System.out.println("目前全部为绿灯状态,无需灯控制器,观察道路车流!"); //打印信息
for ( int i = 0; i < directions.length; i++ ) //对整个数组遍历
{
new Road( directions[i] ); //建立道路类对象并传入数组对应角标参数值
}
}
}
}
/**
* E = East = 东 W = West = 西
* N = North = 北 S = South = 南
* 东西和南北为直线通行类 WE & SN
* 东南和西北为转折通行类 SE & WN
* 每个Lamp枚举元素代表一个方向上的灯,总共有12个方向,共有12个Lamp元素。
* 有如下一些方向上的灯,每两个形成一组,一组灯同时变绿或变红。
* 所以程序代码只需要控制每组灯中的一个灯即可:
* s2n,n2s =
* s2w,n2e ^
* e2w,w2e =
* e2s,w2n ^
* -------
* s2e,n2w x
* e2n,w2s x
* 上面最后两行的灯是虚拟的,由于从南向东和从西向北、以及它们的对应方向不受红绿灯的控制,
* 所以可以假想它们总是绿灯。
* @author 张孝祥-黑马程序员
* @editor 魏安-黑马程序员
*/
enum Lamp //灯枚举
{
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); //定义枚举元素为总是绿灯通行
private boolean lighted; //定义判断变量为绿灯通行开关
private String opposite; //定义字符串变量为与当前灯相同状态对应灯
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("现在 " + this.name() + " 为绿灯,下面共有6个方向能看到汽车穿过!"); //打印此枚举常量名称信息
}
/*当某个灯变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿。*/
public Lamp blackOut() //灭灯功能
{
this.lighted = false; //改变开关值
Lamp nextLamp = null; //定义枚举变量为下个方向灯
if ( opposite != null ) //如果对应灯值不为空
{
Lamp.valueOf(opposite).blackOut(); //对应灯枚举常量递归执行灭灯功能
}
if ( next != null ) //如果当前灯状态改变时下个会变绿灯值不为空
{
nextLamp = Lamp.valueOf(next); //下个方向灯值为当前灯状态改变时下个会变绿灯枚举常量
System.out.println("绿灯从" + this.name() + "切换为" + next); //打印此枚举常量名称信息
nextLamp.light(); //下个方向灯执行亮灯功能
}
return nextLamp; //返回下个方向灯枚举值
}
}
/**
* 对灯进行控制的方法
* @author 张孝祥-黑马程序员
* @editor 魏安-黑马程序员
*/
class LampController //灯控制器类
{
private Lamp currentLamp; //定义枚举变量为当前灯
private Runnable TurnLamp = new Runnable() //建立可运行多线程类对象为转换灯操作
{
@Override //覆写系统功能标记
public void run() //覆写系统运行功能
{
System.out.println("绿灯切换,可以通行了!"); //打印信息
currentLamp = currentLamp.blackOut(); //当前灯值为当前灯执行灭灯功能
}
}; //匿名内部类调用方式
public LampController(Lamp lamp) //类构造函数
{
currentLamp = lamp; //当前灯为枚举调用值
currentLamp.light(); //当前灯执行亮灯功能
/*等10秒后执行且每隔10秒将当前绿灯变为红灯,并让下一个方向的灯变绿*/
ScheduledExecutorService SES = Executors.newScheduledThreadPool(1); //定义规划执行服务为执行器创建线程池
SES.scheduleAtFixedRate(TurnLamp,10,10,TimeUnit.SECONDS); //规划执行服务按照参数执行转换灯操作
}
}
/**
* 每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
* 每条路线上随机增加新的车辆,增加到一个集合中保存。
* 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
* @author 张孝祥-黑马程序员
* @editor 魏安-黑马程序员
*/
class Road //道路类
{
private int Sleeptime = (new Random().nextInt(10) + 1) * 1000; //定义休眠时间
private ArrayList Vechicles = new ArrayList(); //建立数组列表为车辆队伍容器
private String name = null; //定义字符串变量为名字
private Runnable VechiclesRun = new Runnable() //建立可运行多线程类对象为车辆运行操作
{
@Override //覆写系统功能标记
public void run() //覆写系统运行功能
{
for ( int i = 1; i < 1000; i++ ) //定义循环条件
{
try //尝试执行
{
Thread.sleep(Sleeptime); //线程休眠
}
catch ( InterruptedException IE ) //抓住中断异常
{
IE.printStackTrace(); //打印堆栈信息
}
Vechicles.add(Road.this.name + "_" + i); //往容器添加元素
}
}
}; //匿名内部类调用方式
private Runnable Released = new Runnable() //建立可运行多线程类对象为放行操作
{
@Override //覆写系统功能标记
public void run() //覆写系统运行功能
{
if ( Vechicles.size() > 0 ) //如果容器容量值不为空
{
boolean Lighted = Lamp.valueOf(Road.this.name).isLighted(); //判断绿灯通行开关对应枚举值执行判断开关功能
if ( Lighted ) //如果开关值为打开
{
System.out.println(Vechicles.remove(0) + " 有车经过!"); //打印容器移除对应角标值信息
}
}
}
}; //匿名内部类调用方式
public Road(String name) //类构造函数
{
this.name = name; //锁定传入参数变量
/*车辆不断随机上路过程*/
ExecutorService ES = Executors.newSingleThreadExecutor(); //定义执行服务为执行器创建单线程
ES.execute(VechiclesRun); //执行服务执行车辆运行操作
/*每隔一秒检查对应的灯是否为绿,若是,则放行一辆车*/
ScheduledExecutorService SES = Executors.newScheduledThreadPool(1); //定义规划执行服务为执行器创建线程池
SES.scheduleAtFixedRate(Released,1,1,TimeUnit.SECONDS); //规划执行服务按照参数执行放行操作
}
}
学习心得:在巨人的肩膀上,你会做得更好!
其实个人认为,该程序由于要顾及车辆运行的模拟,而不仅仅是交通灯的工作,所以结构就有点复杂了。
毕竟程序的分解是越小块就越具体,用小细节组成大环境会比较好,而如果要一下子考虑全盘因素,肯定有困难。
当然三天时间做出这样的东西,真的很佩服张孝祥老师!
如果只考虑交通灯的工作运行情况,个人思路如下:
1.红灯亮一段时间,绿灯在这段时间内关闭。
2.红灯灭,绿灯打开一段时间。
3.轮流切换,可以用循环,也可以独立设置时间参数。
4.黄灯的工作要看时机而定。
运行参数的关键在于:开关状态和时间长短
以上只是一组灯的运行方式,如果考虑多组灯,请使用多线程或者多步操作完成。无非是逻辑执行而已。
由于本人并非做过这种项目,只能管中窥豹了,如有指教,请在本文评论,谢谢!
原帖:http://blog.csdn.net/zhangxiaoxiang/article/details/6273384
张孝祥老师已逝,可惜!无论世人如何评价他,起码他都是个实实在在做事的人!就凭这一点,就值得世人敬佩!他真的帮助了很多人!