黑马程序员——7K面试题: 交通灯管理系统

时间:2023-02-18 09:38:32

黑马程序员——7K面试题:  交通灯管理系统

-------android培训java培训、期待与您交流!---------- 

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

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

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

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

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

      ………………………………

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

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

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

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

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

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

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

 

——————————题目解析——————————

前言: (注意与其他,O(_)O~

1、北 东 西 南 的单词分别为North  East WestSouth

NEWS新闻单词的由来(来自四面八方的信息)


题目解析: 此交通灯系统主要划分为三块:交通灯、道路(产生/删除车辆)、控制系统

程序代码及解析详见:       

http://dl.vmall.com/c0y38zarjt?v=497798923 Heima(7K面试题)Traffic包源文件

 

2、交通灯的分析详见下图: 理清各方向输出的车辆以及交通灯的交替控制

【交通灯切换思路: 可将红绿灯切换的线路简化为4条,即S2NS2WE2WE2N

 当第一线路灯为绿时其他处于红灯等待,而切换时关闭当前及对应的灯,同时开启/设置下一线路灯为绿;不断交替,交通灯切换时间控制由控制系统负责,通过返回下一盏等用于下一次切换控制即可。】

黑马程序员——7K面试题: 交通灯管理系统

3、道路: 面向对象设计: 谁拥有数据,谁就对外提供操作这些数据的方法。

  因为车辆的驶入与驶出关联的是道路,控制车辆数据的就是道路,因此可将道路看做集合容器,从而:

A驶入到交叉路口出现在道路上的车辆就相当于随机时间向道路集合中添加字符串数据等(也可以定义为Car类,可以扩充未来交通系统程序功能)

B驶离此方向道路可看做在绿灯期间每过指定时间就删除道路集合中一个车辆数据

PS:

(1)由于每个方向道路有通往其他三个方向的车辆,因此一个方向道路要有三个道路集合;

(2)道路集合在无论是红或绿灯时都随机间隙时间添加车辆(可使用传统定时器),而只在绿灯时减少/删除车辆(Thread .sleep(int time)方法)

代码示例:

package com.itheima.Traffic;

import java.util.ArrayList;
import java.util.Arrays;
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;

public class Road {

private List<String> vechicles = new ArrayList<String>();
private String name = null;

//因为每个流向的Road只有一条,所以构造函数应该用单例,使其唯一;
public Road(String name){
this.name = name;

//模拟车辆不断随机上路的过程:
ExecutorService pool = Executors.newSingleThreadExecutor(); //创建一个单线程的执行程序;
pool.execute(new Runnable() {//单线程执行方法,类似Thread;
@Override
public void run() {
//定义循环不断向道路中添加车辆,结束条件是当添加车辆数达到指定数值,例如此处的1000时,线程结束。
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);
timer.scheduleAtFixedRate( //单线程定时器,当Lamp为绿时执行删除道路集合元素动作;
new Runnable() {
@Override
public void run() {
if(vechicles.size()>0){ //判断此道路上是否有车辆未驶离;
boolean lighted = Lamp.valueOf(Road.this.name).isLighted(); //获取当前道路交通灯的颜色是否为绿;
if(lighted){
String lampName = vechicles.remove(0);
String lamp = lampName.substring(0, 3);
System.out.println(lampName+":"+ChineseLamp[l.indexOf(lamp)]+" is traversing !!!"); //打印删除车辆(车辆驶离);
}
}
}},
1, 1, TimeUnit.SECONDS);
}

public static String[] directions = new String[]{
"S2N","S2W","E2W","E2S",
"N2S","N2E","W2E","W2N",
"S2E","E2N","N2W","W2S"
};
public static List<String> l = Arrays.asList(directions);

public static String[] ChineseLamp = new String[]{
"南向北|||直行","南向西————左转","东向西|||直行","东向南————左转",
"北向南|||直行","北向东————左转","西向东|||直行","西向北————左转",
"南向东右转","东向北右转","北向西右转","西向南右转"
};
}
 

4、控制系统: 

(1)控制系统不是主函数类,也是被调用类,需要创建实例对象,但一个交通灯管理系统只有一个控制系统,所以建议LampController类最好是设计成单例

(2)TrafficLamp类拥有自动跳转红绿灯功能,通过定时器循环控制红绿灯间隔时间跳转动作)

(3)控制系统控制启动开始时交叉线路第一盏为绿的交通灯(: 南向北直行灯等)


交通灯类:      //将交通灯的指示定为枚举类的枚举值,便于外部调用(避免错误引用)与简化代码书写;

package com.itheima.Traffic;

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(){}
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){//为什么要判断null? 答:
Lamp.valueOf(opposite).light(); //返回与字符串对应的枚举常量值,并调用该枚举值的变绿方法;
}
System.out.println(this.name()+"lamp is Green,下面总共应该有6个方向的汽车会穿过"); //打印显示语句;
}//this.name() 返回此枚举常量的名称;

//此交通灯变红,即不再是绿灯,同时对应方向的灯也要变红;
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.itheima.Traffic;

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

public class LampController {
//定义每天第一盏为绿的灯的枚举值;
private Lamp currentLamp;

//因为一个交通灯管理系统只有控制系统
public static LampController LC = new LampController(Lamp.S2N); //以枚举值形式设计成单例设计模式(饿汉式);

private LampController(Lamp lamp){ //私有化控制系统构造函数;
currentLamp = lamp;
//指定的交通灯变绿;
currentLamp.light();

/*每隔10秒将当前绿灯变为红灯,并让下一个方向的灯变绿*/
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate( //单线程运行的定时器,指定时间间隔重复运行线程任务;
new Runnable(){
@Override
public void run() {
System.out.println("绿灯改变啦,换方向了.........."); //打印提醒交通灯切换;
//时间间隔将当前绿灯变红,同时启动下一盏为绿的灯,并返回其枚举值用于下一次控制。
currentLamp = currentLamp.blackOut();
}
},10,10,TimeUnit.SECONDS); //第一个long值为延迟时间后开启,第二个long值为重复运行的时间间隔,最后的常量为时间单位;
}
}

5主函数类:

(1)多线程运行12条道路集合;

(2)创建控制系统LampController类实例对象。

package com.itheima.Traffic;

public class MainClass {
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]);
}

//产生整个控制系统: 由于交通系统仅有一个控制系统,因此将LampController设计成饿汉式单例;
LampController lc = LampController.LC;
}
}