黑马程序员-红绿灯交通系统

本文介绍了一种基于枚举和线程技术的红绿灯控制系统设计思路,通过定义灯、路及控制器三个核心对象,实现了红绿灯的自动转换及车辆随机通行的功能。
------- android培训java培训、期待与您交流! ----------

1.当我们不知道该如何去描述一个事物时,我们就需要对事物进行画图分析。这就要求我们多去观察生活中的事物,将他们看做是对象。

2.先写好该如何实现,需要几个事物,有几个事物就有几个对象。首先,红绿灯交通系统顾名思义就需要红绿灯,灯有亮和熄灭。并且一般路口处对面的灯是同时亮和熄灭的。

所以又要定义个相反的灯。都属于灯这个事物,所以我们第一个对象就是灯

3.当灯亮时,说明能通行了,那么车该往哪行驶呢? 就需要路这个事物,来指定车辆可以通行的方向。


如图所示,十字交叉路口是路口中最复杂的一种,但是有特点,即当从北向南的灯亮起时,从南向北的灯也会亮起。

这时,要减少程序的复杂度,我们将向右行驶的车辆全部设为绿灯。不需转换。

就可以把图中的路线改为八条,并且是对称的。当S2W可以行走时,也就表示N2E也可以行走。

就可以将需要确定的路线再一次减少为四组,当第一组亮起时,即两条线路亮起。

那么就开始定义一个灯的事物。此事物中包含对称的灯,还有用这十二条路线来表示这十二条路线的灯的命名。

其中需要十秒进行一次跳转,这里就需要对灯进行操作了,并且灯进行了改变后,车辆的路线也发生改变。所以即操作灯,又操作车的事物我们就抽象定义为系统

假设第一次亮起绿灯的是S2N这条路线,那么N2S也会亮起灯,当过了一段时间后,这两个灯熄灭,再去进行下一个灯的亮起,然后再熄灭再进行。依次重复。

其中,灯亮起时,车辆进行通行,说起来是车在通行,其实就是这条路可以通过,真正用到车这个事物的就是创建和消失,那么这个就可以把车给省略掉,从而改成使这条路上出现一个计数器,来随机生成车辆。这个随机生成车辆和红绿灯切换的时间都属于线程,这里用到线程池的概念。综上所述,真正需要的对象即路,灯,操作路和灯的系统


路:产生一个类似汽车的计数器,来合计创建并消失的该线路(12个)下的车辆的数量。并且使用线程技术

为什么要使用线程呢? 当我进行计数时,为了防止计数的错误,使用了线程。

灯:因为一共有十二条线路,所以需要控制这十二条线路的话就需要十二个灯,这里使用十二条路线的名称来命名灯,就可以用枚举。

新技术产生的目的:使程序开发更简便,完整性更高

当一个灯亮起时,另一个灯也随之亮起,那么就把对应的灯也作为参数。方便操作。假设灯亮为true,灯熄为false,就需要一个bool值来判断灯的状态

但是如果第二个参数是灯这个类对象的话,因为枚举也会定义第二个参数那样的类,第二个如果还没有定义的话难免会发生错误,所以这里我们使用的是灯的名称,即String类型的名称传入进去。进行操作。并且有亮灯方法,以及获取下一个灯的方法,便于实现自动转换灯。

所以灯的代码如下:

public 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 Lamp(String opposite,String next,boolean lighted){
this.opposite = opposite;
this.next = next;
this.lighted = lighted;
}




/*当前灯是否为绿*/
private boolean lighted;
/*与当前灯同时为绿的对应方向*/
private String opposite;
/*当前灯变红时下一个变绿的灯*/
private String next;

//返回灯的状态
public boolean isLighted(){
return lighted;
}

/**
* 某个灯变绿时,它对应方向的灯也要变绿
*/
public void light(){
this.lighted = true;

//上述对应的灯都已设为null,并且已将字符串传入到了对象的第二个参数中。
if(opposite != null){

//枚举的valueOf方法,返回一个对象,该对象的名称由字符串传入

//让对应的灯对象也调用light方法,由于第二个对象并没有第二个参数,为null所以不会再次进行这个方法的递归。
Lamp.valueOf(opposite).light();
}

//此处的六个指的是灯以及对应的灯还有另外四个不受控制的灯
System.out.println(name() + " lamp is green,下面总共应该有6个方向能看到汽车穿过!");

}

/**
* 某个灯变红时,对应方向的灯也要变红,并且下一个方向的灯要变绿
* @return 下一个要变绿的灯
*/
public Lamp blackOut(){
this.lighted = false;
if(opposite != null){
Lamp.valueOf(opposite).blackOut();
}

//如果熄灭了之后就要去调用第三个参数去创建对象,进行新的一轮操作。必须在外面先设null,这样就保证枚举中的对象只有前四个可以进行这个循环操作

//如果不设的话程序会停止。
Lamp nextLamp= null;
if(next != null){
nextLamp = Lamp.valueOf(next);
System.out.println("绿灯从" + name() + "-------->切换为" + next);
nextLamp.light();
}
return nextLamp;
}
}


当灯亮起后,则会产生一个个随机创建的汽车,然后消失。这个就使用路这个事物来进行描述。

public class Road {

//定义一个集合,去存储汽车
private List<String> vechicles = new ArrayList<String>();

private String name =null;

//构造,传入汽车的名称,命名方式:车辆通行的路的方向
public Road(String name){
this.name = name;

//模拟车辆不断随机上路的过程

//使用线程池

//创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行

所以,这个线程池的作用是:控制线程的个数


ExecutorService pool = Executors.newSingleThreadExecutor();

//匿名内部类的形式传入一个参数,并复写run方法
pool.execute(new Runnable(){
public void run(){
for(int i=1;i<1000;i++){
try {

//延迟随机1-10秒,需要处理异常
Thread.sleep((new Random().nextInt(10) + 1) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//添加汽车,并以计数器来进行区分和计数
vechicles.add(Road.this.name + "_" + i);
}
}

});

//每隔一秒检查对应的灯是否为绿,是则放行一辆车

newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

//该线程池的作用:定时,周期性执行

//此处传递了1这个参数,将线程数量设置为1
ScheduledExecutorService timer =  Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){
public void run(){

//当集合中存在车时,将按照路中的名字来进行判断是否对应名称的灯亮
if(vechicles.size()>0){
boolean lighted = Lamp.valueOf(Road.this.name).isLighted();

//如果亮了,就将其从集合中删除,并且返回该元素打印字符串。
if(lighted){
System.out.println(vechicles.remove(0) + " is traversing !");
}
}

}
},
1,  //定时一秒
1,  //之后都为一秒进行周期性执行
TimeUnit.SECONDS);//计时单位,秒

}
}


接下来有了路和灯了,就可以来创建一个系统了

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秒
10, //周期性为10秒
TimeUnit.SECONDS);
}
}


接下来就可以编写main方法来进行验证

public class MainClass {


/**
* @param args
*/
public static void main(String[] args) {

/*产生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();
}


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值