交通灯管理项目模拟了对十字路口交通灯的控制,一般在我们生活中的十字路口是有人行道的,而此项目没有考虑人行道。
具体需求如下:  
1.异步随机生成按照各个路线行驶的车辆。  
        例如:  
        由南向而来去往北向的车辆 ---- 直行车辆  
        由西向而来去往南向的车辆 ---- 右转车辆  
        由东向而来去往南向的车辆 ---- 左转车辆

2.信号灯忽略黄灯,只考虑红灯和绿灯

3.应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。

4.具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。  
        注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。  
        每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。

5.每辆车通过路口的时间为1秒

6、随机生成车辆的时间间隔,以及红绿灯交换时间间隔自定,可以设置。  
       项目编码之前首先要理清项目的需求,在脑海中大致有一个项目的雏形,下面这个图描述了项目中十字路口的情况:
 

分析需求以及上面这个图,可以进行这样的设计:  
一.每条路线上都会出现多辆车,路上要随机增加车,在灯绿期间还要每秒钟减少一辆车,项目中车没有其他的逻辑,可以不把它提炼为一个单独的类,用字符串表示就可以了.
    1、 设计一个Road类来表示路线,每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。  
    2、 每条路线上随机增加新的车辆,增加到一个集合中保存  
    3、 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口

二.每条路线每隔一秒都会检查控制本路线的灯是否为绿,一个灯由绿变红时,应该将下一个方向的灯变绿  
    1、设计一个Lamp类来表示一个交通灯,每个交通灯都维护一个状态:亮(绿)或不亮(红),每个交通灯要有变亮和变黑的方法,并且能返回自己的亮黑状态。
    2、总共有12条路线,所以,系统中总共要产生12个交通灯。右拐弯的路线本来不受灯的控制,但是为了让程序采用统一的处理方式,故假设出有四个右拐弯的灯,只是这些灯为常亮状态,即永远不变黑。
    3、除了右拐弯方向的其他8条路线的灯,它们是两两成对的,可以归为4组,所以,在编程处理时,只要从这4组中各取出一个灯,对这4个灯依次轮询变亮,与这4个灯方向对应的灯则随之一同变化,因此Lamp类中要有一个变量来记住自己相反方向的灯,在一个Lamp对象的变亮和变黑方法中,将对应方向的灯也变亮和变黑。每个灯变黑时,都伴随者下一个灯的变亮,Lamp类中还用一个变量来记住自己的下一个灯。
    4、无论在程序的什么地方去获得某个方向的灯时,每次获得的都是同一个实例对象,所以Lamp类改用枚举来做显然具有很大的方便性,永远都只有代表12个方向的灯的实例对象。
    5、设计一个LampController类,它定时让当前的绿灯变红。

在面向对象设计中有一步很关键,就是提炼出项目中有哪些类,这个需要根据需求与具体的设计,提炼时,可以看需求文档中出现的名词。我们初步设想一下有哪些对象:红绿灯,红绿灯的控制系统,汽车,路线。汽车看到自己所在路线对应的灯绿了就穿过路口吗?不是,还需要看其前面是否有车,看前面是否有车,该问哪个对象呢?该问路,路中存储着车辆的集合,显然路上就应该有增加车辆和减少车辆的方法了。再看题目,我们这里并不要体现车辆移动的过程,只是捕捉出车辆穿过路 口的过程,也就是捕捉路上减少一辆车的过程,所以,这个车并不需要单独设计成为一个对象,用一个字符串表示就可以了。

面向对象设计把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法,这个被称为是专家模式。

此项目设计了4各类,分别是Road、Lamp、LampController、MainClass,下面就来看看这些类是如何设计的:  
Road类  
    1、 每个Road对象都有一个name成员变量来代表方向,有一个vehicles成员变量来代表方向上的车辆集合。  
    2、 在Road对象的构造方法中启动一个线程每隔一个随机的时间向vehicles集合中增加一辆车(用一个“路线名_id”形式的字符串进行表示)。  
    3、 在Road对象的构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,是则打印车辆集合和将集合中的第一辆车移除掉。  
这个类中需要启动一个线程以及定时器,启动线程推荐使用Java5提供的线程库,性能较好些,提供了线程池,线程池是一上来就搞出来许多线程,放在那,当有任务交给他时,从池中找出一个空闲的去执行这个任务,Executors是一个线程工具类,提供了大量的对线程进行支持的静态方法。  
Road类代码如下:

package heimablog.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; /**
* 每个Road对象代表一条路线,总共有12条路线,即系统中总共要产生12个Road实例对象。 每条路线上随机增加新的车辆,增加到一个集合中保存。
* 每条路线每隔一秒都会检查控制本路线的灯是否为绿,是则将本路线保存车的集合中的第一辆车移除,即表示车穿过了路口。
* @author adanac
*/
public class Road { //车并不需要定义一个实体类,将车以字符串的形式定义到数组中即可
//通过分析车的特点,车有增加和减少的方法,所以链表结构是符合车的存储特点的
//为什么不写ArrayList集合呢 因为面向接口编程
/*接口 List<E>
所有超级接口:
Collection<E>, Iterable<E>
所有已知实现类(部分):
AbstractList, ArrayList, AttributeList, LinkedList, Vector*/ private List<String> vehicls = new ArrayList<String>(); private String name; //在构造方法中不断的创建车
public Road(String name) {
this.name = name; //线程库,使用线程库来创建一个线程,相当于一个线程池
ExecutorService pool = Executors.newSingleThreadExecutor(); //相当于new一个Runnable的实现类对象
pool.execute(new Runnable() { @Override
public void run() {
for (int i = 1; i < 8000; i++) {
try {
Thread.sleep(1000 * (new Random().nextInt(10) + 1));//sleep随机数,随机值1~10
} catch (InterruptedException e) {
e.printStackTrace();
}
vehicls.add(Road.this.name + "_" + i);//内部类访问外部类,可以在构造函数的参数String前加final,也可以使用外部类名.this.变量名
}
}
}); //定时器每隔1s检查绿灯是否变亮
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(new Runnable() { @Override
public void run() {
//定义变量lighted判断当前线路的灯是否是绿灯
boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
if (lighted) {
//如果是绿灯并且检查到路上有车,则放行第一辆车
if (vehicls.size() > 0) {
System.out.println(vehicls.remove(0));
}
}
}
}, 1, 1, TimeUnit.SECONDS);
}
} /**
*Road类中使用了Java多线程:类Executors主要的工厂方法,也是创建线程池的方法,创建一个单线程化的Executor):newSingleThreadExecutor()
*创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
*可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
*/

Lamp类
    1、系统中有12个方向上的灯,在程序的其他地方要根据灯的名称就可以获得对应的灯的实例对象,综合这些因素,将Lamp类用java5中的枚举形式定义更为简单。
   2、每个Lamp对象中的亮黑状态用lighted变量表示,选用S2N、S2W、E2W、E2N这四个方向上的Lamp对象依次轮询变亮,Lamp对象中还要有一个oppositeLampName变量来表示它们相反方向的灯,再用一个nextLampName变量来表示此灯变亮后的下一个变亮的灯。这三个变量用构造方法的形式进行赋值,因为枚举元素必须在定义之后引用,所以无法再构造方法中彼此相互引用,所以,相反方向和下一个方向的灯用字符串形式表示。
   3、增加让Lamp变亮和变黑的方法:light和blackOut,对于S2N、S2W、E2W、E2N这四个方向上的Lamp对象,这两个方法内部要让相反方向的灯随之变亮和变黑,blackOut方法还要让下一个灯变亮。
   4、除了S2N、S2W、E2W、E2N这四个方向上的Lamp对象之外,其他方向上的Lamp对象的nextLampName和oppositeLampName属性设置为null即可,并且S2N、S2W、E2W、E2N这四个方向上的Lamp对象的nextLampName和oppositeLampName属性必须设置为null,以便防止light和blackOut进入死循环。
Lamp类代码如下:

package heimablog.traffic;

/**
* 每个Lamp元素代表一个方向上的灯,总共有12个方向,所有总共有12个Lamp元素。
* 有如下一些方向上的灯,每两个形成一组,一组灯同时变绿或变红.
* 程序代码只需要控制每组灯中的一个灯即可:
* s2n,n2s
* s2w,n2e
* e2w,w2e
* e2s,w2n
* s2e,n2w
* e2n,w2s
* 上面最后两行的灯是虚拟的,由于从南向东和从西向北、以及它们的对应方向不受红绿灯的控制,
* 所以,可以假想它们总是绿灯。
* @author adanac
*/
public enum Lamp { //定义12条线路,每个枚举元素各表示一个方向的控制灯
//关键在于模拟右转弯的灯始终是亮着的,以统一模型
//每个枚举元素各表示一个方向的控制灯
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); /* 与当前灯同时为绿的对应方向(为了避免传递的错误,我们将灯Lamp变为字符串,传递对应灯的名字字符串) */
private String opposite;
/* 当前灯变红时下一个变绿的灯 */
private String next;
/* 当前灯是否为绿 */
private boolean lighted;
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() + " 灯亮了,有6个方向的车在移动");
} // 让灯变红,并且让对应方向的灯也变红,也要让下一个方向的灯变绿
public Lamp blackOut(){
this.lighted = false;
if (opposite != null) {
System.out.println("绿灯由" + name() +" -----> " + next);
Lamp.valueOf(opposite).blackOut();
} Lamp nextLamp = null;
//定义一个避免死循环的解决办法
if (next != null) {
nextLamp = Lamp.valueOf(next);
nextLamp.light();
}
return nextLamp;
}
}

LampController类
    1、整个系统中只能有一套交通灯控制系统,所以,LampController类最好是设计成单例。
   2、LampController构造方法中要设定第一个为绿的灯。
   3、LampController对象的start方法中将当前灯变绿,然后启动一个定时器,每隔10秒将当前灯变红和将下一个灯变绿。
LampController代码如下:

package heimablog.traffic;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; //定义一个灯的控制器
public class LampController { //定义一个变量:当前的灯
private Lamp currentlLamp;
//定义构造方法, 构造方法上来 应该让某一个灯变绿
public LampController() {
currentlLamp = Lamp.S2N;
currentlLamp.light(); //做个定时器,让每隔10s绿灯变红灯,并让下一个方向的灯变绿
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(new Runnable() { @Override
public void run() {
currentlLamp = currentlLamp.blackOut();
}
}, 10, 10, TimeUnit.SECONDS);
}
}

MainClass类
    1、用for循环创建出代表12条路线的对象。
   2、接着创建出LampController对象,并调用其start方法。
代码如下:

package heimablog.traffic;

public class MainClass {

	public static void main(String[] args) {

		//创建12条路线的对象
String[] directions = {
"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();
}
}

执行结果如下:

N2S 灯亮了,有6个方向的车在移动
S2N 灯亮了,有6个方向的车在移动
S2E_1 is travelling!
E2N_1 is travelling!
W2S_1 is travelling!
N2W_1 is travelling!
N2S_1 is travelling!
N2S_2 is travelling!
W2S_2 is travelling!
S2N_1 is travelling!
N2W_2 is travelling!
N2W_3 is travelling!
绿灯由S2N -----> S2W
N2E 灯亮了,有6个方向的车在移动
S2W 灯亮了,有6个方向的车在移动
N2E_1 is travelling!
S2W_1 is travelling!
S2E_2 is travelling!
E2N_2 is travelling!
N2E_2 is travelling!
N2E_3 is travelling!
N2W_4 is travelling!
W2S_3 is travelling!
S2E_3 is travelling!
S2W_2 is travelling!
E2N_3 is travelling!
N2E_4 is travelling!
N2E_5 is travelling!
E2N_4 is travelling!
S2E_4 is travelling!
N2W_5 is travelling!
绿灯由S2W -----> E2W
W2E 灯亮了,有6个方向的车在移动
E2W 灯亮了,有6个方向的车在移动
E2W_1 is travelling!
W2E_1 is travelling!

总结:

  • 成功的模拟了对交通灯的控制,这个项目中最重要的就是,分析业务逻辑,画出相应的分析图:总共有12条路线,为了统一编程模型,假设每条路线都有一个红绿灯对其进行控制,右转弯的4条路线的控制灯假设称为常绿状态。
  • 另外,其他的8条线路是两两成对的,可以归为4组,所以程序只需考虑图中标注了数字号的4条路线的控制灯的切换顺序,这4条路线相反方向的路线的控制灯跟随这4条路线切换,不必额外考虑。没有这一步这个项目就无从入手了,这也告诉我们做一个项目之前,要理解需求,理清思路,让项目在大脑中有一个大致的雏形,然后再去编码,这样一步一步的才能成功!

Java交通灯系统的更多相关文章

  1. java交通灯管理系统项目

    交通灯管理系统   模拟实现十字路口的交通灯管理系统逻辑,具体需求如下: 异步随机生成按照各个路线行驶的车辆. 例如: 由南向而来去往北向的车辆 ---- 直行车辆 由西向而来去往南向的车辆 ---- ...

  2. Java——交通灯

     /* * 交通灯管理系统的 项目需求: * 模拟实现:  十字路口的交通灯系统逻辑,  具体需求如下: *  异步随机生成按照各个路线行驶的车辆. *  信号灯忽略黄灯.  只考虑红灯和绿灯. ...

  3. 黑马程序猿_7K面试题之交通灯系统

    交通灯信号模拟系统 一.概述 模拟实现十字路口的交通灯管理系统逻辑,详细需求例如以下:(需求直接来源于老师的文档) ①      异步随机生成依照各个路线行驶的车辆. 比如: 由南向而来去往北向的车辆 ...

  4. 黑马程序员——【Java高新技术】——案例:交通灯管理系统

    ---------- android培训.java培训.期待与您交流! ---------- 一.交通灯管理系统的项目需求 Ø 异步随机生成按照各个路线行驶的车辆 例如: 由南向而来去往北向的车辆 - ...

  5. 黑马程序员:Java编程_7K面试题之交通灯管理系统

    =========== ASP.Net+Android+IOS开发..Net培训.期待与您交流!=========== 模拟实现十字路口的交通灯管理系统逻辑,具体需求如下: 异步随机生成按照各个路线行 ...

  6. java 7K交通灯管理系统面试题

    交通灯管理系统 模拟实现十字路口的交通灯管理系统逻辑.详细需求例如以下: 1. 异常随机生成依照各个路线行驶的车辆.    比如:    由南向而来去往北向的车辆----直行车辆    由西向而来去往 ...

  7. Java基础---Java---面试题---交通灯管理系统(面向对象、枚举)

    交通灯管理系统的项目需求: 模拟实现十字路口的交通灯管理系统逻辑,具体需求如下: 1.异步随机生成按照各个路线行驶的车辆  例如:   由南向而来去往北向的车辆-----直行车辆   由西向而来去往南 ...

  8. java 面试题之交通灯管理系统

    需求: 交通灯管理系统的项目需求 Ø 异步随机生成按照各个路线行驶的车辆. 例如: 由南向而来去往北向的车辆 ---- 直行车辆 由西向而来去往南向的车辆 ---- 右转车辆 由东向而来去往南向的车辆 ...

  9. 黑马程序员_JAVA之交通灯管理系统

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 1.一.需求:模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:  1.异步随机生成按照各个路 ...

随机推荐

  1. 通栏导航栏的制作,综合使用CSS属性,代码不超过30行

    这篇文章,小编带领大家一同做一个利用CSS技术实现的导航栏.通过这个导航栏的制作,希望大家能够对前几篇文章中学习到的CSS属性能有一个整体的认识,并能够达到灵活运用的程度. 承接文章:灵活控制块级元素 ...

  2. 小程序收集formid跳转后收集不到

    为了突破微信小程序模板消息限制,需收集到足够的推送码,即每次提交表单时获取到的formId.一个formId代表着开发者有向当前用户推送模板消息的一次权限.当表单组件中的属性report-submit ...

  3. Django QuerySet 进阶

    QuerySet 进阶 阅读本文你可以学习到什么 1. 查看 Django queryset 执行的 SQL(1部分) 2. 获得的查询结果直接以类似list方式展示(2,3 部分) 3. 如何在dj ...

  4. poj2406(后缀数组)

    poj2406 题意 给出一个字符串,它是某个子串重复出现得到的,求子串最多出现的次数. 分析 后缀数组做的话得换上 DC3 算法. 那么子串的长度就是 \(len - height[rnk[0]]\ ...

  5. Xamarin XAML语言教程隐藏文件使用Progress属性设置进度条

    Xamarin XAML语言教程隐藏文件使用Progress属性设置进度条 Xamarin XAML语言教程隐藏文件中使用Progress属性设置进度条进度,开发者除了可以在XAML中使用Progre ...

  6. hdu 1500 Chopsticks DP

    题目链接:HDU - 1500 In China, people use a pair of chopsticks to get food on the table, but Mr. L is a b ...

  7. Bluetooth篇 开发实例之十 官网的Bluetooth Chat sample app.

    运行的时候,会报错: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Action ...

  8. ASIHTTPRequest学习(三)

    刚刚开始学习ASIHttpRequest,今天通过自己写的一个小demo分享一下学习心得. 首先,要想在ios项目中使用ASIHttpRequest,必须添加下列框架和类库: ASIHttpReque ...

  9. Android 版 Facebook 登录

    Android 版 Facebook SDK 让用户可以通过 Facebook 登录注册您的应用.通过 Facebook 登录您的应用时,用户可以向应用授予权限,以便您可以检索信息或以用户的身份在 F ...

  10. flask的session研究和flask-login的session研究

    1.httpie的安装:https://github.com/jakubroztocil/httpie#macos 2.http://python.jobbole.com/87450/ 3.http: ...