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

简介:

                                 交通灯信号模拟系统


一、概述

模拟实现十字路口的交通灯管理系统逻辑,详细需求例如以下:(需求直接来源于老师的文档)

①      异步随机生成依照各个路线行驶的车辆。

比如:

       由南向而来去往北向的车辆 ---- 直行车辆

       由西向而来去往南向的车辆 ---- 右转车辆

       由东向而来去往南向的车辆 ---- 左转车辆

       。

②      信号灯忽略黄灯,仅仅考虑红灯和绿灯。

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

④        详细信号灯控制逻辑与现实生活中普通交通灯控制逻辑同样。不考虑特殊情况下的控制逻辑。

注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。

⑤       每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。

⑥      随机生成车辆时间间隔以及红绿灯交换时间间隔自定,能够设置。

⑦      不要求实现GUI,仅仅考虑系统逻辑实现,可通过Log方式展现程序执行结果。



二、需求分析:

纵观整个系统的需求我们能够得出下面结论:在一个十字路口。涉及到了车、灯以及路。能够把他们之间的关系模拟以下。把路设计成为一个存储车辆的容器,车辆来了,存入容器内,等到车辆将要通过某一条路上的红绿灯亮起,车辆启动。相当于从容器中取出车辆,通过十字路口。以下再细致地介绍:

四面拐弯的设为永远都是绿灯,也就是说四面什么时候都能够转。也就是S--E 。E--N,N--W,W--N,所以我们在以下就不加以考虑了

我们先从S---N看起:如果有S--N的车能够开动了,那么这个时候就有N--S的车也能够开动了。也就是说仅仅要S--N能够通车,那么N--S也能够通车

S--N的绿灯停止了,S--N将不能再通车。相同,N--S也不能通车。

下一个绿灯就是S--W:S--W能够通车,则N--E也能够通车

S--W绿灯停止:下一个绿灯是E--W:E--W能够通车,W--E也能够通车

E--W绿灯停止:下一个绿灯是E--S;E--S能够通车,W--N也能够通车

E--S绿灯停止:下一个绿灯又回到S--N。如此循环。

从上面的分析能够得出。我们仅仅须要四个方向上的交通灯状态就能够控制整个交通系统。由于我们发现他们都是成对出现。比方说我们仅仅要知道S--N是绿灯,那么N--S也应该是绿灯,S--W是绿灯,N--E也应该是绿灯,还有四个角上的拐弯也固定的绿灯,所以我们仅仅需控制四个方向上的四盏等就能够控制整个系统了。


三、面向对象的分析与设计

①每一条路上的车辆在某一时段内能够随机添加车辆,在绿灯到来的时候,要降低该路上的车辆数。当然是在绿灯亮起期间有顺序降低车辆。

某个时刻。可能有随意的车辆到随意的一条路上,这里我们使用到了12盏灯。全部我们要如果有十二条路(实际上仅仅有8条)。且每条上某个时刻都有可能有车辆增加该路上等待(或者直接通过,可是得是绿灯且其前面没有车的时候),也就是说我们要为每一盏灯创建一个存储车辆的容器,当某一盏灯亮起的时候,相应容器中的汽车就能够通过了,

②每一条路上在指定时间都回去检查该线路上红绿灯是否为绿。不为绿,不同意车辆通过本线路,在某一条线路上的红绿灯表红的时候,要记得把下一个方向上的红绿灯变绿。

设计一个Lamp类来表示一个交通灯。每一个交通灯都维护一个状态:亮(绿)或不亮(红)
总共同拥有12条路线。所以。系统中总共要产生12个交通灯。

右拐弯的交通灯为常绿状态,即永远不变红。

不管在程序的什么地方去获得某个方向的灯时。每次获得的都是同一个实例对象,所以Lamp类改用枚举来做显然具有非常大的方便性,永远都仅仅有代表12个方向的灯的实例对象。

设计一个枚举类,它定时让当前的绿灯变红。

四 用到的类的编写

①Road类的编写

因为有12条线路,这里开启一个线程池。分配线程池中的线程去为每一条线路添加车辆

同一时候还要监视红绿灯(以便知道那个线路上的车辆能够启动),那就要开启一个线程去监视该红绿灯的情况,同一时候在监视中还要得到下一个变绿的灯。

package cn.itcast.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> ArrRoad = new ArrayList<String>();
	//面相接口编程,提高程序的可扩展性
	 
	String name = null; //指定是那条路上的的产生的车辆
	
	
	 
	public Road(String name ){
		this.name = name;
		//能够产生车辆了
		//因为是在某个时刻。每一个方向上都有可能产生车辆。所以这里要用到多线程的思想
		ExecutorService  thread =  Executors.newCachedThreadPool();	//创建线程池
		thread.execute(new Runnable() {//任务到来时,从线程池中选择一个线程来运行该任务
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 1000; i++) {
					//为了达到更加接近实际。这里设置一个随机数,也就是说在某个时间段内随时都有可能有汽车开到路上
					try {
						
						Thread.sleep((new Random().nextInt(10)+1)*1000);//在1s--10s中有车上路
						ArrRoad.add(Road.this.name+(i+1));//指定是那条路上的第几辆车,
						//这里有个知识点,匿名内部类訪问外围类的成员变量的时候,有两种方法,能够将外围类的成员变量设置为final,也也可像这里写的这样
						//类名.this.成员变量
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					
					
				}
				
			}
		});
		//我是这样想的。某条路上有车上来。当遇到路灯的时候。那么这条路上的车就会开走,也就是说这条路上的车会降低
		
		//这里也要单独开启一个线程来运行个监视(监视绿灯)
		//dispatch(调度)
		//  创建一个线程池,它可安排在给定延迟后运行命令或者定期地运行。这里就相当于说是规定什么时候去观察红绿灯的情况
		ScheduledExecutorService  dispatch= Executors.newScheduledThreadPool(1);
		//这里的意思是在 创建并运行一个在给定初始延迟1s后首次启用的定期操作,在1s后有运行new Runnable的实现类对象的方法
		
		dispatch.scheduleAtFixedRate(
				new Runnable() {
					
					@Override
					public void run() {
						// TODO Auto-generated method stub
						//先推断推断,name路上的灯是否亮了
						boolean light =Lamp.valueOf(Road.this.name).isstate();
						
					//在这里想要知道那条公路上的车该走了,首先得到该路上的车所要走的那条路线上的灯
						if(ArrRoad.size() > 0){
						if(light){
							System.out.println(Road.this.name+"路上的车"+ArrRoad.remove(0)+"飞快通过");
							
						}
						}
						
						
					}
				}, 
				1, 
				1, 
				TimeUnit.SECONDS
				);
		
	}
	
}

②Lamp类的编写

系统中有12个方向上的灯。在程序的其它地方要依据灯的名称就能够获得相应的灯的实例对象,综合这些因素,将Lamp类用java5中的枚举形式定义更为简单。
每一个对象中与当前对象所代表的的灯相相应的灯用correlight表示,该灯是否有下一个灯用next表示,当前灯是否为绿灯用flag表示。
添加了三个方法。也就是开启绿灯。关闭绿灯以及获取当前灯的状态是绿灯还是红灯。
在这里。我设置了Lamp的对象的三个參数的意义分别为:是否有与之相应的灯,为null者表示没有,是否有下一个灯,为null表示没有。当前灯是否为绿。没false表示为红灯。


实现细节在源码中讲述的非常清楚:

package cn.itcast.Traffic;



//使用枚举来描写叙述每一个路口路口上的灯
//这里我们先从南到北说起,S,N,E,W代表南、北、东、西,S2N即使南到北
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);
	//这里有必要解释一下S2N("N2S","S2W",false):
	//參数一指定了与当前灯向相应的灯,參数二,指定该灯的下一个灯,
	//參数三,指定该灯是否亮起绿灯
	//corresponding(相应)
	String correlight = null;
	String next = null;
	boolean flag;
	Lamp nextlight;
	Lamp(String correlight,String next,boolean flag ){
		  this.correlight = correlight;
		  this.next = next;
		  this.flag = flag;
	}
	public boolean isstate(){
		return flag;
		
	}
	//指定开启那条路上的绿灯
	public void oppen(){
		flag = true;
		if(correlight != null){ //推断当前要开启的灯是否有与之相应的灯,有,则开启,没有则不用开启,也防止的死循环
			Lamp.valueOf(correlight).oppen();//这里的意思是得到当前对象相应的灯并打开该绿灯
                                                         //Lamp.valueOf(String str )返回带指定名称的指定枚举类型的枚举常量。

这里得到的也就是和correlight同名的枚举常量  } } //指定关闭那条路上的绿灯 public Lamp close(){ flag = false; if(correlight != null){ Lamp.valueOf(correlight).close(); } //关闭的同一时候,要打开该灯的下一个灯,假设有的话 if(next !=null){ nextlight = Lamp.valueOf(next); nextlight.oppen(); } return nextlight; } }



③LampManage类的编写

该类为一个交通灯的总控制。为了达到方便 ,我们创建仅仅有一个线程的线程池,该线程池用来控制红绿灯的交换时间。我这里初始灯是由S2N,在覆盖了接口Runnable的run方法中,要获得Lamp类中close方法返回的下一个绿灯的对象。

由此就能够不断循环控制整个交通灯,

这里设置了每隔10s绿灯就切换到下一条线路上。

package cn.itcast.Traffic;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class LampManage {
	//该类用来管理整个交通灯的运作
	//也就是搞一个定时器,让交通灯能够互相切换
	
	Lamp lamp ;
	
	public LampManage(){
		//先指定以那条路上的灯開始
	
		lamp = Lamp.S2N;
		
		ScheduledExecutorService  timer =	Executors.newScheduledThreadPool(1);//设置一个定时器,也就是开启一个线程
      	timer.scheduleAtFixedRate(
      			new Runnable() {
					
					@Override
					public void run() {
						// TODO Auto-generated method stub
					System.out.println(lamp+"方向的灯亮了。。

。。。。"); System.out.println("相相应的有6个方向能够通车了 各自是 :S2E,E2N,N2W,W2S,"+lamp.correlight+","+lamp); lamp = lamp.close();//开启10s后运行该段代码,也就是关闭当前的交通灯开启下一个交通灯 } }, 10, 10, TimeUnit.SECONDS); } }


④MainManage类的编写

该类的编写非常easy,就是构造12条线路,调用交通灯的总控制类。使得能够在每条线路随机添加汽车且调动了红绿灯来控制车辆之间的来往

package cn.itcast.Traffic;
public class MainManage {
	
	
	//产生十二个红绿灯
	public static void main(String[] args) {
    String lamp[]={"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};
		
		for(int i=0;i<lamp.length;i++){
			new Road(lamp[i]);
			 
		}
		
		//启动交通灯
		new LampManage();
		
	}
	

}


到此交通灯模拟就算是结束,,,看似简单。却是用到了非常多的基础知识,尤其是面向对象的基础。想要学得更好。基础是不可缺少的。







本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5351407.html,如需转载请自行联系原作者

相关文章
|
8月前
|
缓存 NoSQL 关系型数据库
|
7天前
|
消息中间件 存储 缓存
招行面试:如何让系统抗住双十一 预约抢购活动?10Wqps级抢购, 做过吗?
本文由40岁老架构师尼恩撰写,针对一线互联网企业如得物、阿里、滴滴等的面试题进行深度解析。文章聚焦于如何设计系统以应对大促活动中的预约抢购场景,涵盖从预告到支付的完整流程。尼恩通过系统化、体系化的梳理,帮助读者提升技术实力,轻松应对高并发挑战,并提供了详细的架构设计和解决方案。文中还分享了《尼恩Java面试宝典》等资源,助力求职者在面试中脱颖而出,实现“offer直提”。更多内容及PDF资料,请关注公众号【技术自由圈】获取。
|
3月前
|
C语言
经典面试题:嵌入式系统中经常要用到无限循环,怎么样用C编写死循环呢
在嵌入式系统开发中,无限循环常用于持续运行特定任务或监听事件。使用C语言实现死循环很简单,可以通过`while(1)`或`for(;;)`的结构来编写。例如:`while (1) { /* 循环体代码 */ }`,这种写法明确简洁,适用于需要持续执行的任务或等待中断的场景。
|
3月前
|
存储 消息中间件 缓存
系统设计面试参考-设计Spotify系统
【10月更文挑战第4天】支持用户将自己喜欢的音乐、专辑、播放列表等分享到社交媒体平台,如 Facebook、Twitter、Instagram 等。分享内容可以包括音乐链接、封面图片、简介等信息,吸引更多的用户来使用 Spotify 系统。同时,系统可以跟踪分享的效果,如点击量、转化率等,以便评估社交分享对系统推广的贡献。
|
6月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
73 1
|
6月前
|
消息中间件 算法 NoSQL
面试题Kafka问题之Kafka保证系统的可用性如何解决
面试题Kafka问题之Kafka保证系统的可用性如何解决
51 0
|
7月前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
94 2
|
6月前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
67 0
|
6月前
|
设计模式 存储 缓存
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
69 0
|
6月前
|
设计模式 存储 缓存
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
49 0

热门文章

最新文章