交通灯项目需求如下:
1. 要求模拟一个十字路口的交通灯控制系统,具体要求如下:
2. 用程序模拟出各个路线上汽车奔跑的过程;
3. 只考虑绿色和红色信号灯,忽略黄色信号灯;
4. 只考虑左转信号灯,不用考虑右转信号灯;
5. 具体信号灯控制逻辑应当与真实交通灯控制逻辑一样,东西向与南北向交替放行;
6. 每辆车通过时间为1s;
7. 随机生成车辆时间间隔以及红绿灯交替切换时间间隔自定,可以设置;
8. 通过Log方式在控制台实现模拟
面向对象的分析和设计
视频中老师一再强调,要着重学习培养自己的编程思想,就是要根据开发需要,将客观世界中的各个元素模拟成相应合适的程序元素,并且要确定好这些各个元素间的关系,在这个程序开发中,有路线,方向,信号灯,车等对象,为了能够清楚的理解交通灯的具体运作过程,我们画图来透彻理解:
为了清楚说明问题,我们假设每条路线都有一个红绿灯来指示交通,并用字母代表汉字来说明路线的方向。
假设先开始南北方向是绿灯可以通行,那么按照项目的要求,具体通行切换的过程应该是:
N2S和S2N(南北方向车辆直行)-----àN2W和S2E(南北方向车辆左转)-----àW2E和E2W(东西方向车辆直行)-----àW2N和E2S(东西方向车辆左转)
另外四条右转路线S2E,W2S,E2N,N2W因为不管在哪个路线上,车辆走右转路线总是在同向的车道上,不会与其他车辆发生冲突,所以右转路线上的信号灯可以假设是常绿的。
根据以上的分析,就可以知道各条路线的红绿灯怎么切换了,老师在视频中讲过,面向对象的核心思维是谁拥有数据就让谁拥有对外提供操作这些数据的方法,路线作为一个对象,它应该有成员变量红绿灯,汽车,而红绿灯自身也作为一个对象,它应该要拥有控制灯亮灯灭的方法,这个方法还应当有个按时按刚才分析的切换顺序轮换指定灯亮的功能。
下面分析路线对象的特点:
用Road类来模拟路线,共有12条路线,那么必须要有12个路线对象来代表这些路线,
每条路线上随机增加新的车辆,增加到一个集合中保存,每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
再看红绿灯对象的特点:
用Lamp类来模拟红绿灯,一共要有12个灯分别对应12条路线,其中除了四只右转向的红绿灯不用考虑控制问题,设置其属性为常绿,其他的8个红绿灯按照刚才上面分析的结果,是2个灯一对,变红变绿一起切换,因此可以从这四组灯中各取出一个来控制,每组另一个灯跟随变化即可,当一组灯刚一变红,则按照切换的顺序,下一组灯立即变绿,所以Lamp类中应该还有一个变量来记录下一栈灯,以便控制,每次路线要用到的红绿灯总是相应的固定灯对象,可以把灯对象设计成枚举常量,用另一个控制工具类LampController,定时让当前的灯变红。
编写Road类:
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;
/**
* 每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。
* 每条路线上随机增加新的车辆,增加到一个集合中保存。
* 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
* @author 张孝祥 www.it315.org
*
*/
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();//建立一个线程池,产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行。
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) {
e.printStackTrace();
}
vechicles.add(Road.this.name + "_" + i);//内部类访问成员变量,用全称,用括号中的字符串表示一辆车的名字
}
}
});
//每隔一秒检查对应的灯是否为绿,是则放行一辆车
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);//建立一个线程调度对象,参数1表示池中保持一个线程,产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行
// 在只有一个线程的时候,他和newSingleThreadExecutor()作用是一样的。
timer.scheduleAtFixedRate(
new Runnable(){
public void run(){
if(vechicles.size()>0){
boolean lighted = Lamp.valueOf(Road.this.name).isLighted();//检查绿灯是否亮,内部类访问外部类的非Final型的变量name,要加全称
if(lighted){
System.out.println(vechicles.remove(0) + " is traversing !");//在集合中删除最先加入的元素,表示车辆已经通过
}
}
}
},
1,//过1单位时间执行run()
1,//执行完等待1单位时间
TimeUnit.SECONDS);//定义时间的单位
}
}
编写Lamp类:
package com.isoftstone.interview.traffic;
/**
* 每个Lamp元素代表一个方向上的灯,总共有12个方向,所有总共有12个Lamp元素。
* 有如下一些方向上的灯,每两个形成一组,一组灯同时变绿或变红,所以,
* 程序代码只需要控制每组灯中的一个灯即可:
* s2n,n2s
* s2w,n2e
* e2w,w2e
* e2s,w2n
* s2e,n2w
* e2n,w2s
* 上面最后两行的灯是虚拟的,由于从南向东和从西向北、以及它们的对应方向不受红绿灯的控制,
* 所以,可以假想它们总是绿灯。
* @author 张孝祥 www.it315.org
*
*/
/**/
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;
if(opposite != 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();
}
Lamp nextLamp= null;
if(next != null){
nextLamp = Lamp.valueOf(next);
System.out.println("绿灯从" + name() + "-------->切换为" + next);
nextLamp.light();
}
return nextLamp;
}
}
控制灯的类LampController
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(){
System.out.println("来啊");
currentLamp = currentLamp.blackOut();
}
},
10,
10,
TimeUnit.SECONDS);
}
}
主类:
package com.isoftstone.interview.traffic;
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();
}
}