您的位置:首页 > 职场人生

黑马程序员________7k面试题之交通灯

2012-10-18 11:33 204 查看
------- android培训java培训、期待与您交流! ----------

需求

交通灯管理系统

模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:

1、异步随机生成按照各个路线行驶的车辆。

例如:

由南向而来去往北向的车辆 ---- 直行车辆

由西向而来去往南向的车辆 ---- 右转车辆

由东向而来去往南向的车辆 ---- 左转车辆

。。。

2、信号灯忽略黄灯,只考虑红灯和绿灯。

3、应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。

4、具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。

注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。

5、每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。

6、随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。

7、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

通过分析可以知道十字路口一共存在 每个路口3个方向 * 一共4个路口 = 12个方向。而其中每个路口中向右拐的车不受信号灯控制,而剩下的八个等又是两两对应,所以我们只需要完成四个灯的逻辑关系即可。

看过题目想自己先小试牛刀一下再看视频,就先自己做了一个。实现界面如下




每隔一秒会自动刷新控制台,显示每个方向当前信号灯状态以及此方向的路上滞留车辆。实现代码如下:

package com.itheima.trafficlamp;

import java.util.Arrays;
import java.util.Random;

public class trafficLampDemo {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

Resource resource=Resource.getResource();
Arrays.fill(resource.lamps,0,resource.lamps.length,Lamp.RED);//初始化开始都为红灯

new Thread(new AmountAuto(resource)).start();
new Thread(new ShowU(resource)).start();
new Thread(new LampAuto(resource)).start();
new Thread(new TrafficAuto(resource)).start();

}

}

class Resource{//定义一个资源类,里面有各个路口的车辆数和各个路口的红绿灯状态,由于此类对象需要在程序中具有唯一性,所以采用单例模式
int[] population=new int[12];//数组每一个元素的值代表一个一个路口上堆积的车辆数量
Lamp []lamps=new Lamp[12];//数组每一个元素值为当前信号灯的状态

private static Resource resource=null;
private Resource(){}
public static Resource getResource(){
if(resource==null){
synchronized(Resource.class){//双重判断,在安全性保障的前提下保证程序的运行效率
if(resource==null){
resource=new Resource();
return resource;
}
}
}
return resource;

}
}

enum Lamp{//定义信号灯为一个枚举
RED(){
public Lamp nextLamp(){//覆写类中抽象方法获取下一个灯为绿灯

return GREEN;
}
public String getName(){
return "红灯";
}
},GREEN(){
public Lamp nextLamp(){//覆盖类中抽象方法获取下一个灯为红灯

return RED;
}
public String getName(){
return "绿灯";
}
};
private Lamp(){}

int stayTime=2;//定义绿灯的时间

public abstract Lamp nextLamp();
public abstract String getName();
}

class AmountAuto implements Runnable//这是一个对资源中汽车数量进行操作的一个线程
{
Resource resource;
AmountAuto(Resource resource)//他需要将程序内公共的资源接收进程序
{
this.resource=resource;
}
public void run()
{
Random random=new Random();//创建一个随机源

while(true){
try {
Thread.sleep(400);//每隔400毫秒进行一次无限循环
} catch (InterruptedException e) {
e.printStackTrace();
}
int index=random.nextInt(12);//循环过程得到一个0-11的随机数
resource.population[index]+=1;//将这个随机数作为资源中amount数组的角标对该角标的元素进行+=1操作,即模拟了随机产生一个去随机方向的车
}
}
}

class LampAuto implements Runnable{//这是一个对资源中交通灯的状态进行操作的一个线程
Resource resource;

public LampAuto(Resource resource) {//他也需要将公共资源包接收进来
this.resource = resource;
}

public void run(){
resource.lamps[2]=Lamp.GREEN;//根据分析2,5,8,11方向均为右转,所以信号灯一直为绿灯,不参与信号灯变化循环
resource.lamps[5]=Lamp.GREEN;
resource.lamps[8]=Lamp.GREEN;
resource.lamps[11]=Lamp.GREEN;
while(true){
resource.lamps[0]=resource.lamps[0].nextLamp();
resource.lamps[6]=resource.lamps[6].nextLamp();
if(resource.lamps[4].equals(Lamp.GREEN))//这是上个循环结束4号信号灯应该为绿灯,做判断是因为初始值为红灯。
resource.lamps[4]=resource.lamps[4].nextLamp();
if(resource.lamps[10].equals(Lamp.GREEN))//信号灯x和x+6同理,以下不在赘述
resource.lamps[10]=resource.lamps[10].nextLamp();

try {
Thread.sleep(Lamp.GREEN.stayTime*1000);
} catch (Exception e) {
e.printStackTrace();
}
resource.lamps[0]=resource.lamps[0].nextLamp();//每隔两秒关闭上一个
resource.lamps[6]=resource.lamps[6].nextLamp();//并打开下一个
resource.lamps[1]=resource.lamps[1].nextLamp();
resource.lamps[7]=resource.lamps[7].nextLamp();

try {
Thread.sleep(Lamp.GREEN.stayTime*1000);
} catch (Exception e) {
e.printStackTrace();
}
resource.lamps[1]=resource.lamps[1].nextLamp();
resource.lamps[3]=resource.lamps[3].nextLamp();
resource.lamps[7]=resource.lamps[7].nextLamp();
resource.lamps[9]=resource.lamps[9].nextLamp();

try {
Thread.sleep(Lamp.GREEN.stayTime*1000);
} catch (Exception e) {
e.printStackTrace();
}
resource.lamps[3]=resource.lamps[3].nextLamp();//八秒是一个循环,4个线路各有两秒的绿灯时间
resource.lamps[4]=resource.lamps[4].nextLamp();
resource.lamps[9]=resource.lamps[9].nextLamp();
resource.lamps[10]=resource.lamps[10].nextLamp();

try {
Thread.sleep(Lamp.GREEN.stayTime*1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

class TrafficAuto implements Runnable{//这是是根据信号灯状态而改变车辆数量的一个线程
Resource resource;

public TrafficAuto(Resource resource) {//同样需要操作同一个资源
this.resource = resource;
}
public void run(){
while(true){
try {
Thread.sleep(1000);//每隔一秒进行一次无限循环,有则执行,模拟车辆需要一秒通过
} catch (Exception e) {
e.printStackTrace();
}
for(int x=0;x<resource.lamps.length;x++){//遍历信号灯数组
if(resource.lamps[x].equals(Lamp.GREEN)){//如果有绿灯
if(resource.population[x]!=0)//且该路上的车辆不能为零(零再减就成负数,不符显示)
resource.population[x]-=1;//数量减1
}
}

}
}

}

class ShowU implements Runnable//这是一个在控制台上显示我们当前状态的线程
{
public ShowU(Resource resource) {//显示的是公共资源
this.resource=resource;
}

Resource resource;

public void run()
{
while(true){
try {
Thread.sleep(1000);//每隔一秒刷新一次
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(
resource.lamps[0].getName()+"\t\t"+resource.lamps[1].getName()+"\t\t"+resource.lamps[2].getName()+"\r\n"+
"南-->北"+resource.population[0]+" \t"+"南-->西"+resource.population[1]+" \t"+"南-->东"+resource.population[2]+"\r\n\r\n"+
resource.lamps[3].getName()+"\t\t"+resource.lamps[4].getName()+"\t\t"+resource.lamps[5].getName()+"\r\n"+
"东-->西"+resource.population[3]+" \t"+"东-->南"+resource.population[4]+" \t"+"东-->北"+resource.population[5]+"\r\n\r\n"+
resource.lamps[6].getName()+"\t\t"+resource.lamps[7].getName()+"\t\t"+resource.lamps[8].getName()+"\r\n"+
"北-->南"+resource.population[6]+" \t"+"北-->东"+resource.population[7]+" \t"+"北-->西"+resource.population[8]+"\r\n\r\n"+
resource.lamps[9].getName()+"\t\t"+resource.lamps[10].getName()+"\t\t"+resource.lamps[11].getName()+"\r\n"+
"西-->东"+resource.population[9]+" \t"+"西-->北"+resource.population[10]+" \t"+"西-->南"+resource.population[11]+"\r\n"+
"--------------------------------------------------------------"
);
//打印每一路上的信号灯状态和等待车辆
}
}

}

之后又看了张孝祥老师的视频,感觉印象最深的就是张老师的面向对象的思想。也就是谁拥有数据,就把操作该数据的方法定义给谁。比如人在黑板上年画圆,画圆的方法用到了原点坐标,半径,这些数据是圆的,所以该方法应该定义给圆。
那么根据张老师的思路是将车作为路的数据,又每个路对象对自己路上的车进行随机自增。然后每隔一秒对该路方向上的信号灯进行检查,如果是绿,则自减。这在思想上和我在思路上如出一辙,只是实现方式不同。但是张老师在信号灯上的思想却充分体现了面向对象的编程思想。我的程序中信号灯的枚举只单单作为一个状态而体现,就如flag的true和false一样,操作该状态的方法定义在了信号灯控制器上。但是根据面向对象的思想,信号灯的状态是信号灯的数据,所以该方法应该定义下信号灯内。信号灯内应该有以下几种方法:1,改变自己的的状态,同时改变与自己对应那一个方向灯的状态。2,记住自己的状态,并可返回。3,记住自己下一个逻辑顺序的信号灯。然后就可以设计信号灯控制器了,这个类因为在程序中时唯一的,所以用单例模式,如同我自己写的程序中resource类一样,该类对象唯一作用就是开启一个线程,调用信号灯中的方法,定时改变各个方向上的信号灯。

以下是张老师的信号灯类和信号灯控制器类

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;
}
}

package com.isoftstone.interview.traffic;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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);
}
}



这是张老师的实现界面,感觉没有我直观呀吼吼!但是自己的设计思想上有一定的不成熟,继续努力

。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: