------- android培训、java培训、java学习型技术博客、期待与您交流! ----------
7k面试题之交通灯
1 题目要求
模拟实现十字路口的交通灯管理系统逻辑,具体有以下需求
1). 异步随机生成按照各个路线行驶的车辆
举例说明如下:
[1]. 由南向北行驶的车辆 ----直行车辆
[2]. 由西向南行驶的车辆 ----右转车辆
[3]. 由东向南行驶的车辆 ----左转车辆
…
2). 信号灯颜色选择范围
忽略黄灯,只考虑红灯和绿灯
3). 左转车辆和右转车辆的要求
[1]. 左转车辆受到控制信号灯的控制
[2]. 但是右转车辆不受信号灯控制
【注意】具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
4). 其他说明
[1]. 南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
[2]. 每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
[3]. 随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。
[4]. 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
2 题意分析
路口分析以及简化
(1). 十字路口线路图
(2). 图例和相关术语
[1]. 方向简写
N --->北 S --->南 E--->东 W --->西
[2]. “2”是英文单词“to”的意思
e.g. N2S表示从北到南的线路
[3]. 对应线路
{1}. 一条线路和他的对应线路可以同时放行的时候,不会出现两条线路上的车辆的相互撞车的事件。
{2}. 这里面将一条线路的对应线路在后面用“()”括起来。
{3}. 图中颜色相同的线路表示“一对”对应线路
[4]. 图中线路的分类
这个十字路口中一共有12条线路存在,分成以下三类:
{1}. 右转线路 (4条)
图中四个拐角的褐色线路。分别是:N2W、W2S、S2E和E2N
{2}. 左转线路 (4条)
图中两条粉色和两条红色的线路。分别是:N2E(S2W)和W2N(S2E)
{3}. 直行线路 (4条)
图中两条绿色和两条蓝色的线路。分别是: N2S(S2N)和E2W(W2E)
[6]. 线路的要求
{1}. 直行+左转的8条线路必须遵守这条线路上的交通灯的指示进行运行和停止
{2}. 右转的4条线路不用按照红绿灯*运行。
(2). 线路分析简化设计思路
[1]. 简化原则
{1}. 这里总共有12条路线,因此每条路线作为一个对象存在。
{2}. 为了统一编程模型,可以假设每条路线都有一个红绿灯对该条线路的车进行控制。
{3}. 其中右转弯的4条路线的控制灯可以设为常绿状态。
{4}.其他8条线路是两两成对的,可以归为4组。
所以程序只需考虑图中标注了数字的4条路线的控制灯的切换顺序,这4条路线对应方向的路线的控制灯跟随标记数字的4条路线切换,不必额外考虑。
[2]. 根据题目需求进行线路切换次序确定
{1}. 需求是“南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。”
{2}. 线路简化之后,只考虑了两个方向出发的车:
{2}1. 从S方向出发的车:S2W和S2N这两个方向的线路 ---- 要求是先放行直行再放行左转,所以这里面是S2N先放行,S2W后放行
{2}2. 从E方向出发的车:E2W和E2S这两个方向的线路 ---- 要求是先放行直行再放行左转,所以这里面是E2W先放行,E2S后放行
{2}3. 从S方向或者E方向那个开始都可以,因为这些线路的交通灯是循环切换的。因此这里面从S方向开始。
所以线路切换是
S2N【1】---> S2W【2】---> E2W【3】 ---> E2S 【4】--->S2N【1】 --->...循环
算上每条线路的对应线路,下面给出完整的线路切换是:
S2N(N2S)【1】---> S2W(N2E)【2】---> E2W(W2E)【3】 ---> E2S(W2N)【4】--->S2N(N2S)【1】 --->...循环
[3]. 简化之后的线路图如下
这里面需要真实考虑的线路只有直行的2条+ 左转的2条 (一共4条线路)
3. 面向对象的分析初步
1). 通过需求分析名词,定义出类
(1). 类的提取
[1]. 需求中出现的类:交通灯、线路、车辆
[2]. 额外的类 (通过分析需求)
需求是车在线路上运行,运行的时候要观察该条线路上的交通灯的颜色的变化。这个交通灯的颜色怎么切换?靠谁去切换?肯定不是交通灯自己切换,而是交通灯靠一个交通灯控制器去切换。------第四个类是:交通灯控制器
【总结】所需的类:交通灯、线路、车辆和交通灯控制器四个类
(2). 面向对象的分析与设计的原则
[1]. 面向对象设计把握一个重要的经验
分析的出来的哪个类有哪些数据,这个类就对外提供操作这些数据的方法。
【注意】数据就是指的是这个类的成员变量
[2]. 从一个比较复杂的类,也就是字面上看着会有很多成员的类进行分析。因为这样的类可以直接分析出来很多成员,有利于接下来对其他的类进行分析
[3]. 为每一个类进行两件事情的分析
{1}. 分析这个类的成员变量
一定要分析需求,根据这个需求自行描绘出来这个事件发生的过程,在这个过程中分析出这个类可能的“聚合”或者“包含”的其他类的成员变量即可。
{2}.分析这个类的成员方法
根据分析出来的成员变量和包含需求事件发生的过程为这个类的对象添加需要对外访问的方法。
【注意1】成员变量和成员方法绝对不是一下子就添加完成的,边分析边做项目,边添加。
【注意2】就是每一个提取出来的类不一定必须要自定义一个单独的类。如果Java的核心类库有合适的类型匹配,那就可以直接选来使用。
3 交通灯类Lamp的程序编写
1) 编写过程中注意事项
(1). Lamp类应该采用枚举更好
所有地方的交通灯都是一样的,具有12个代表12条线路的交通灯,不会多也不会少,因此这种固定不变的类Lamp类采用JDK5的枚举来做显然具有很大的方便性
(2). 一对一的映射关系可以使用String类型来替代Lamp成员属性
由于Lamp类中的表示对应线路的属性和下一条切换线路的属性都是一对一的关系,因此一一对一的关系可以采用String类型来简化替代Lamp类聚合的表示对应的Lamp类的实例和表示下一条交通灯的Lamp类实例。
2). Lamp类代码示例
(1).Lamp的点亮和灭掉交通灯的方法
增加让Lamp变亮和变黑的方法:light和blackOut,对于S2N、S2W、E2W、E2N这四个方向上的Lamp对象,这两个方法内部要让相反方向的灯随之变亮和变黑,blackOut方法还要让下一个灯变亮。
(2). Lamp类示例代码
package cn.isoftstone.interview.traffic; /** * 每个Lamp元素代表一个方向上的灯,总共有12个方向,所有总共有12个Lamp枚举元素。 * 程序代码只需要控制每组灯中的一个灯即可:----括号中的表示该线路的交通灯对应的线路的交通灯 * S2N(N2S)、S2W(N2E)、 E2W(W2E)、 E2S(W2N) -----受红绿灯控制的交通灯 * S2E(N2W) 和E2N(W2S) -----不受红绿灯控制的交通灯 * @author XinmingZhang */ public enum Lamp { //每个枚举元素各表示一个线路方向上的控制灯 //左转的两条主路+ 两条直行线 ----初始化这些主路的灯光是灭的---Red 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), //四条右拐的路 常绿 ---四个右转弯方向的灯,因为其不受红绿灯控制,所以可以假设它们总是绿灯 N2W(null, null, true), W2S(null, null, true), S2E(null, null, true), E2N(null, null, true); //当前灯的状态,是否为绿 private boolean lighted =false; //当前线路交通灯的对应线路的交通灯 private String opposite =null; //一对一 所以使用String的name替代 //当前线路交通灯的被切换到下一个交通灯 private String next =null; //一对一 所以使用String的name替代 /** * 构造函数 * @param opposite ----当前线路对应线路的交通灯的名字 * @param next --------当前线路的下一条线路的交通灯的名字 * @param lighted -----这条线路的交通灯初始化的时候被点亮(代表绿灯) */ private Lamp(String opposite, String next, boolean lighted){ this.lighted =lighted; this.opposite =opposite; this.next =next; } /** * lighted属性的Getter * @return---获取当前线路的交通灯的状态 */ public boolean isLighted() { return lighted; } /** * 点亮该灯----点亮表示将灯变成绿色的 * 某个灯变绿时,它对应方向的灯也要变绿 */ public void light(){ this.lighted =true; //同时要点亮对应路段的交通灯 if(this.opposite !=null){ LampoppositeLamp =Lamp.valueOf(opposite); oppositeLamp.light(); } System.out.println(name()+" lamp is green, 下面可以看到6个方向看到汽车穿过!"); } /** * 灭掉该灯----也就是将该路段的交通灯变红 * @return----返回下一站切换的路灯 */ public Lamp blackOut(){ //当前灯变红,对应方向的灯也变红 this.lighted =false; //同时要灭掉对应路段的灯 if(this.opposite !=null){ LampoppositeLamp =Lamp.valueOf(opposite); oppositeLamp.blackOut(); } //点亮下一对应的灯 LampnextLamp =null; if(next !=null){ nextLamp=Lamp.valueOf(next); nextLamp.light(); System.out.println("绿灯从"+ name()+"--->切换为"+next); } return nextLamp; } }
3) 线路类Road程序编写
(1). 删除方法 ------代表车辆穿过这条线路
每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
(2). 增加车辆和删除车辆的方法简化
这两个方法仅仅被使用一次之后即可,不需要被其他的方法调用。因此简化程序,就直接放入到Road类的构造方法即可。
(3). Road类构造方法设计思路
2 先设计成单线程的构造方法
先生成车(初始化成员对象) ----> 在放行这些车写成单线程程序如下:
public Road(Stringname){ this.name =name; //随机的时间间隔生成1000辆车 for(int i=0; i<1000; i++){ try { int randomTime =(newRandom().nextInt(10)+ 1)*1000; Thread.sleep(randomTime); this.vehichles.add(this.name+"_"+ i); } catch(InterruptedException e) { // TODOAuto-generated catch block e.printStackTrace(); } } //每隔一秒放行一辆车 while(vehichles.size()>0){ //1s放行一辆车 try { Thread.sleep(1000); } catch(InterruptedException e) { e.printStackTrace(); } //观察是否可以通过这条路 ---- 观察这条路上的对应的交通灯对应的状态 boolean lighted =Lamp.valueOf(this.name).isLighted(); if(lighted){ System.out.println(vehichles.remove(0)+" istraversing!"); } } }