---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------
另外一到面试题,从学习中,对面向对象和枚举类的应用有了更进一步掌握。
1、 系统需求
(1)异步随机生成按照各个路线行驶的车辆。
例如:
由南向来,去往北向的车辆.......直行车辆
由西向来,去往南向的车辆.......右转车辆
由东向来,去往南向的车辆.......左转车辆
......
(2)信号灯忽略黄灯,只考虑红灯和绿灯。
(3)应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。
(4)具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。
注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。
(5)每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。
(6)随机生成车辆时间间隔,红绿灯交换时间间隔自定,可以设置。
(7)不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
2、 需求分析
先看下十字路口车辆的行车路线图:
如图所示,并结合实际我们能知道:
(1) 十字路口分东,南,西,北四个方向,每个方向又分左右两行道,那么车辆能行驶直行道,左转道,右转道。
(2) 那么,总的行驶路线如图所示为12条,由于右转向道并不与其它道路交错,需求也说明不受信号灯控制,在不考虑黄灯的情况下,我们除去右转向道的4条道路,则只考虑8条道路(图中红蓝线标示)的信号灯。
(3) 实际中,要求不论哪个方向的车辆都得靠右行驶,那么我们得出:
西向东,东向西的相对行驶的车辆,由于都各自只靠右行道行驶,所以并不会出现行驶道路的交错,并且道路对称,于是我们只考虑西向东的一路,从而可推演出相向的东向西的一路。
同理,南向北,北向南也只考虑一路,西向北和东向南的左转道我们也只考虑一路。最终,在8条道路中,我们划分成4组道路,由4条道路表示(即图中标号的4条)。
(4) 由这4条道路,我们根据对称规则,推出另外相向行驶的4条道路,那么只需考虑4条路的信号灯状态变化。
3、 系统分析
先考虑面向对象的对象构建,依据谁拥有数据谁就拥有该对象的方法的原则。
(1) 信号灯被封装成对象,这里采用了枚举。信号灯需维护一个状态,绿或红,那么每个交通灯要有变绿或变红的方法,且能返回红绿的状态。
(2) 分析时为统一配置,12条道各自绑定一个信号灯。右转道本无信号灯,我们假想有一个常绿的信号灯。由上述分析,我们只设置4条道的信号灯,其它信号灯跟随这4个信号灯变化而变化。那么信号灯除了要有自身状态外,还应在当前灯状态变化时通知下一个灯的变化。
(3) 创建一个信号控制器,控制器中有一个定时器线程,每隔一段时间改变当前灯的状态,从而推动12个信号灯的循环。
(4) 车辆的细节不是本系统考虑的重点,这里用字符串表示车辆。车辆没通过十字路口,车辆的数量会随机增加,车辆通过路口,车辆数量将减少一辆,这里用列表集合的增删模拟。车辆的增加和减少是互不影响,这里将用两个线程分别对车辆的增加和减少进行控制。车辆的数量控制需要两线程同步,那么两线程的车辆信息用同一个列表集合存储。
(5) 车辆的增减信息,是属于路的数据,所以由路的对象管理存储车辆的列表集合以及增删两线程。增线程随机时间向队列添加车辆,减线程获取道路对应信号灯的状态判断何时减少车辆,即车辆通过。
4、 基本类说明
Lamb类:枚举类,枚举了12条道路的信号灯,有获取信号灯当前状态的方法,改变状态的方法,通知下一个灯的方法。
LambController类:信号灯控制器,拥有一个定时器线程,隔一段时间改变信号灯的状态。
MainClass类:主调用类,创建12条道路后,开启信号灯的控制器。
Road类:管理车辆的增删队列,并且管理随机时间增加车辆的线程和根据对应信号灯状态减少车辆的线程。
5、 示例代码
MainClass类:
/** 主调用类,创建路对象和信号灯控制器 */ 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]); } //信号灯控制器 new LambController(); } }
LambController类:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** 信号灯控制器,定时器将每隔一段时间变换信号灯状态 */ public class LambController { private Lamb currentLamb; public LambController(){ currentLamb = Lamb.S2N; //灯亮,即灯绿 currentLamb.light(); ScheduledExecutorService timer =Executors.newScheduledThreadPool(1); //固定频率的定时器 timer.scheduleAtFixedRate( new Runnable(){ public void run(){ currentLamb= currentLamb.blacOut();//灯变化 } }, 10, 10, TimeUnit.SECONDS); } }
Road类:
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; /** 该类拥有车辆的存储队列,有增加车辆的线程和车辆减少的线程。 */ public class Road { List<String> vechicles = new ArrayList<String>(); private String name = null; public Road(String name){ this.name = name; ExecutorService pool =Executors.newSingleThreadExecutor(); //增加车辆数量的线程 pool.execute(new Runnable(){ public void run(){ for(int i =0; i<1000;i++){ //随机时间,用于随机增加车辆 delayTime(); vechicles.add(Road.this.name+ "_"+ i); } } }); ScheduledExecutorService timer =Executors.newScheduledThreadPool(1); //固定频率的定时器,减少车辆线程 timer.scheduleAtFixedRate( new Runnable(){ public void run(){ removeVechicle(); } }, 1, 1, TimeUnit.SECONDS ); } //车辆通过,即从队列出移除 public void removeVechicle(){ if(vechicles.size() > 0){ //获取该车要参考的灯状态 boolean lighted =Lamb.valueOf(name).isLighted(); if(lighted){//绿灯通行 System.out.println(vechicles.remove(0)+" is traversing !"); } } } public void delayTime(){ try { //1秒到10秒的随机时间 Thread.sleep((newRandom().nextInt(10)+1)*1000); } catch (InterruptedException e){ e.printStackTrace(); } } }
Lamb类:
public enum Lamb{ //举例:当前(直行):南-->北,对向:北-->南,下一个:南-->西(即左转) 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 boolean lighted;//状态 private String opposite;//对向灯 private String next;//下一个将亮的灯 //参数分别为,对向灯,下一个灯,当前灯的状态 private Lamb(String opposite, Stringnext, boolean lighted ){ this.opposite = opposite; this.next = next; this.lighted = lighted; } //不带参构造方法 private Lamb(){} //判断灯状态 public boolean isLighted(){ return lighted; } //灯变亮(变绿) public void light(){ this.lighted = true; //对向灯同状态 if(opposite != null) Lamb.valueOf(opposite).light();//字符串转枚举对象 System.out.println(name() +" lamb is green, 下面应该有6个方向能看到汽车穿过 !"); } //灯变黑(即变红灯) public Lamb blacOut(){ this.lighted = false; //对向灯同状态 if(opposite != null) Lamb.valueOf(opposite).blacOut(); //下一个灯变亮 Lamb nextLamb = null; if(next != null){ nextLamb =Lamb.valueOf(next); System.out.println("绿灯从 "+ name() + "----->切换为" + next); nextLamb.light(); } return nextLamb; } }
---------------------- ASP.Net+Android+IO开发S、 .Net培训、期待与您交流! ----------------------