首先要做需求分析,然后设计这个项目的结构和功能。具体来说,就是这个项目应该包含哪几个类,这些类与类之间的联系,以及这些类中分别需要实现的方法。要弄明白这些不是一件容易的事,坦白说,我自己想一天也未必能想明白。呵呵,这就是能力啊。张老师把这些都分析的非常清楚:
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;
}
}
3.红绿灯的控制系统:LampController类
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
);
}
}
4.项目的主类:MainClass
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(); //红绿灯控制器
}
}
看完第一遍视屏,感觉就像看天书一样,云里雾里的,没明白啥意思,看第二遍的时候,就有感觉了,但是还有一些不明白的地方,再看一遍的时候,就明白了