首先要做需求分析,然后设计这个项目的结构和功能。具体来说,就是这个项目应该包含哪几个类,这些类与类之间的联系,以及这些类中分别需要实现的方法。要弄明白这些不是一件容易的事,坦白说,我自己想一天也未必能想明白。呵呵,这就是能力啊。张老师把这些都分析的非常清楚:
1.总共有12条路线,为了统一编程模型,可以假设每条路线都有一个红绿灯对其进行控制,右转弯的4条路线的控制灯可以假设称为常绿状态,另外,其他的8条线路是两两成对的,可以归为4组,所以,程序只需考虑1S2N 2S2W 3E2W 4E2S 这4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。
2.为了使思路更加清晰,开始只考虑从南面开来的车辆:
1)右转(S2E)的信号灯永远是绿灯;
2)同方向等待车辆应先放行直行车辆(S2N)而后放行左转车辆(S2W)。
3.面向对象的分析与设计:
1)初步设想一下有哪些对象:红绿灯,红绿灯的控制系统,汽车,路线。
2)汽车看到自己所在路线对应的灯绿了就穿过路口吗?不是,还需要看前面是否有车,看前面是否有车,该问哪个对象呢?该问路,路中存储着车辆的集合,显然路上就应该有增加车辆和减少车辆的方法了。(面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法。)再看题目,我们这里并不要体现车辆移动的过程,只是捕捉出车辆穿过路口的过程,也就是捕捉路上减少一辆车的过程,所以,这个车并不需要单独设计成为一个对象,用一个字符串表示就可以了。在1中初步设想的对象就减少了一个。这个项目只需要3个对象即可。
3)在这个十字路口,有且仅有12盏红绿灯,因此红绿灯可以用枚举来实现。其中右转的四盏灯常绿,剩下的八盏灯可以分为4组。因此只需要考虑4盏灯的红绿变化。
4.跟随老师一起编写代码:
1)Road类:
public class Road { private List<String> vehicles = new ArrayList<String>(); //用这个List来装载这条路上的车 private String name = null; //这条路线的名字。同时也是它所对应的灯的名字。 public Road(String name) //构造方法,每创建一条路时都必须为它命名 { this.name = name; //启动一个线程,不停地向这条路上增加车辆。额,这种启动线程的方法以前没用过,多多学习下。 ExecutorService pool = Executors.newSingleThreadExecutor(); pool.execute(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { try { Thread.sleep((new Random().nextInt(10)+1) * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // vehicles.add(name + "_" + i);这样写编译会报错,因为这里访问的是这个构造方法里的局部变量。在匿名内部类里访问局部变量,这个局部变量必须用final修饰符修饰。所以这里有两种改法:1.加final修饰符,2.在这里访问外部类的成员变量。 vehicles.add(Road.this.name + "_" + i); //访问外部类的成员变量。 } } }); //启动一个定时器。这种方法我以前也没用过。额,这种方法的确比TimeTask什么的好用多了。 ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate( //这个方法需要四个参数 new Runnable() //参数1:任务。如果是绿灯,并且路上有车,就移走一辆车 { @Override public void run() { if (vehicles.size()>0) { boolean lighted = Lamp.valueOf(Road.this.name).isLighted(); // if (true == lighted) 这行代码的不妥之处在于画蛇添足 if (lighted) { System.out.println(vehicles.remove(0) + "is traversing ! "); } } } }, 1, //参数2:多少时间后开始执行任务 1, //参数3:每隔多少时间执行一次任务 TimeUnit.SECONDS //参数4:前面这两个时间的单位 ); } }
2.Lamp枚举
package com.isoftstone.interview.traffic; public enum Lamp { S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false), //有业务逻辑的4个灯 N2S(null,null,false) ,N2E(null,null,false) ,W2E(null,null,false) ,W2N(null,null,false) , //和上面一一对应的4个灯 S2E(null,null,true) ,E2N(null,null,true) ,N2W(null,null,true) ,W2S(null,null,true) ; //常绿的4个灯 private boolean lighted; //用来表示灯的状态。true为绿灯,false为红灯 private String opposite;//这里要把相对应的Lamp用字符串代替,是因为S2N(N2S)这种写法可能会报错,Cannot reference a field before it is defined. private String next; //当前灯变红时,下一盏应该变绿的灯 private Lamp(String opposite,String next,boolean lighted) { this.opposite = opposite; this.next = next; this.lighted = lighted; } 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个方向能看到汽车穿过 !"); //这六个方向是指:当前路线, //当前路线的对应路线,以及4个常绿路线。 } // 让当前灯变红的方法,同时检查当前灯有没有对应灯,若有,需将对应灯也变红。然后,将当前灯的下一盏灯变绿。 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; } }
public class LampController { private Lamp currentLamp; public LampController() { currentLamp = Lamp.S2N; //最开始设定S2N这个灯是绿的 currentLamp.light(); ScheduledExecutorService timer = Executors.newScheduledThreadPool(1); timer.scheduleAtFixedRate( new Runnable() //每隔10s将当前灯变暗,同时将currentLamp变为下一盏灯。 { @Override public void run() { System.out.println("灯控定时器"); currentLamp = currentLamp.blackOut(); } }, 2, 2, TimeUnit.SECONDS ); } }
public class MainClass { public static void main(String[] args) { //new出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(); //红绿灯控制器 } }