---------------------- android培训、java培训、期待与您交流! ----------------------
今天看了张老师讲的交通灯管理系统,体会了一个项目的基本完整过程,意识到需求分析在一个项目中的重要性,并且在该系统的视频中更进一步体会到了枚举的作用,更深入地理解了面向对象的思想,学习了一个添加线程的新方法:用Executors关键字来创建。
开始一个项目,首先需要对项目进行需求分析,这一步非常关键,如果这一步的分析不清晰或者不到位,后面的开发过程会非常困难。这一步也可以间接考验是否完全了解题目意思和题目想要达到的效果。在需求分析的过程中,如果思路不清晰,可以借助画图来理解和分析问题,我觉得这是一个非常好的方法。就拿今天这个系统来说,如果只是去思考,思绪会很混乱,但是用画图来表示,则一目了然。根据题目意思,张老师画的图如下,一看就非常清晰。
根据不同情况,可以对过程简化,比如本系统的12条路线,其中4条是随时可以通车的,不用考虑别的情况。其他8条路线都是两两对应的,可以归为4组来考虑,即简化为4条路线。
初步分析,本题有4个对象:红绿灯,红绿灯控制系统,路线,汽车。
由于面向对象设计有一个重要的经验,即谁拥有数据,谁就对外提供操作这些数据的方法。因此,分析过程如下:
(1) 每条路线上都会出现多辆车,路线上需随机增加新的车,在灯绿期间还要每秒钟减少一辆车(需要检查红绿灯状态)。
(2) 每条路线每隔一秒都会检查控制本路线的灯是否为绿,一个灯由绿变红时,应该将对应的灯也变红,同时将下一个方向的灯和下一个方向对应的灯变绿。
分析完成了,下面来实现程序。
①Road类
——每个Road对象都有一个name成员变量来代表方向,有一个vehicles成员变量来代表方向上的车辆集合。
——在Road对象的构造方法中启动一个线程每隔一个随机的时间向vehicles集合中增加一辆车(用一个 “路线名_id”形式的字符串进行表示)。
——在Road对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,是则打印车辆集合和将集合中的第一辆车。
代码如下:
package com.isoftstone.interview.traffic;
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 {
private List<String> vechicles =new ArrayList<String>();
private String name=null;
public Road(String name){
this.name=name;
//添加线程Executors
ExecutorService pool=Executors.newSingleThreadExecutor();
pool.execute(new Runnable(){
public void run(){
for(int i=1;i<1000;i++)
{
try {
Thread.sleep((new Random().nextInt(10)+1)*1000);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
vechicles.add(Road.this.name+"_"+i);//添加车辆到集合中
}
}
});
//newScheduledThreadPool制作定时器
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){
public void run(){
if(vechicles.size()>0){
//boolean lighted=true;
boolean lighted=Lamp.valueOf(Road.this.name).isLighted();
if(lighted){
System.out.println(vechicles.remove(0)+" is traversing!");
}
}
}
},
1,
1,
TimeUnit.SECONDS);//第4个参数TimeUnit.SECONDS表示第2,3个参数的计量单位为秒
/*timer.schedule(
new Runnable(){},
1,
arg2)*/
}
}
②Lamp类
——用枚举来定义灯的名称
——每个Lamp对象中的亮黑状态用lighted变量表示,选用S2N、S2W、E2W、E2N这四个方向上的Lamp对象依次轮询变亮,Lamp对象中还要有一个oppositeLampName变量来表示它们相反方向的灯,再用一个nextLampName变量来表示此灯变亮后的下一个变亮的灯。这三个变量用构造方法的形式进行赋值,因为枚举元素必须在定义之后引用,所以无法再构造方法中彼此相互引用,所以,相反方向和下一个方向的灯用字符串形式表示。
——增加让Lamp变亮和变黑的方法:light和blackOut,对于S2N、S2W、E2W、E2N这四个方向上的Lamp对象,这两个方法内部要让相反方向的灯随之变亮和变黑,blackOut方法还要让下一个灯变亮。
——除了S2N、S2W、E2W、E2N这四个方向上的Lamp对象之外,其他方向上的Lamp对象的nextLampName和oppositeLampName属性设置为null即可,并且S2N、S2W、E2W、E2N这四个方向上的Lamp对象的nextLampName和oppositeLampName属性必须设置为null,以便防止light和blackOut进入死循环。
代码如下:
package com.isoftstone.interview.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(String opposite,String next,boolean lighted)
{
this.opposite=opposite;
this.next=next;
this.lighted=lighted;
}
private Lamp()
{
}
private boolean lighted;
private String opposite; //对应的灯的名字
private String next;
public boolean isLighted(){
return lighted;
}
//灯变绿的方法
public void light()
{
this.lighted=true;
// System.out.println(name()+" lamp is green: 下面总共应该有6个方向能看到汽车穿过!");
if (opposite!=null)
{
Lamp.valueOf(opposite).light();
//opposite.light();
}
System.out.println(name()+" lamp is green: 下面总共应该有6个方向能看到汽车穿过!");
//在枚举中,灯的名字用name()方法
//opposite.lighted=true;
}
//灯变红的方法:把自己和相应的灯变红,把下一个灯变绿
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;
}
}
③Lampcontroller类
——整个系统中只能有一套交通灯控制系统,所以,LampController类最好是设计成单例。
——LampController构造方法中要设定第一个为绿的灯。
——LampController对象的start方法中将当前灯变绿,然后启动一个定时器,每隔10秒将当前灯变红和将下一个灯变绿。
代码如下:
package com.isoftstone.interview.traffic;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class LampController {
private Lamp currentLamp;
public LampController(){
currentLamp=Lamp.S2N;
currentLamp.light();
//newScheduledThreadPool制定定时器,返回ScheduledExecutorService类型
ScheduledExecutorService timer=Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("来啊");
currentLamp=currentLamp.blackOut();
}
},
10,
10,
TimeUnit.SECONDS);
}
}
④MainClass类
——用for循环创建出代表12条路线的对象。
——接着再获得LampController对象并调用其start方法。
代码如下:
package com.isoftstone.interview.traffic; public class MainClass { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub 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(); } }
学习过程中发觉eclipse的一个好处,当你使用某些未引入的关键字是,eclipse有功能可以自动引入类所在的包.该程序中用Executors方法来创建线程,它的newSingleThreadExecutor()方法返回一个ExecutorService对象,在接受Runnable对象来创建一个线程,该方法似乎不需要用start()方法来启动线程。Executors也可以制定定时器,它的newScheduledThreadPool()返回一个ScheduledExecutorService 对象,调用scheduleAtFixedRate方法可以实现定时器的效果.让一定时间代码重复执行.如只需一定时间后执行一次,则可以使用schedule方法,它们最后一个参数都是接受计量单位.
---------------------- android培训、java培训、期待与您交流! ----------------------