经典面试项目--交通灯管理系统

2024-06-16 08:18

本文主要是介绍经典面试项目--交通灯管理系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目由来:

该项目原本是软通动力的一道面试题,交由面试者带回去自行完成,审核通过后即通过面试,当然现在不可能再作为面试题了。不过这个项目还是非常有实践意义的,在网络上传播广泛,从中我们可以学习面向对象的程序设计精髓,对于掌握Java SE基础的初学者而言意义更甚。好了,下面就具体看一看这个项目。

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

例如:

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

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

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

       。。。

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

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

具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。

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

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

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

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

思路分析与程序设计

从项目需求中可以发现主要有交通灯、各个路线的行驶车辆两大类。

路线分析以及时间设定:结合现实情况分析可知一个方向向其他三个方法各有一条路线,既然是十字路口那么线路总共是3x4=12条。

红绿灯规则:右转信号灯常绿,每辆车通过路口的时间为1秒钟,并且后面有紧随其后开来的车,这里我们设定随机生成一个车辆的时间为2-10秒钟,设定每个路口直行绿灯亮20秒,左转绿灯亮15秒,于是可以得到直行红灯时间为50秒,左转红灯时间为55秒。示意图如下:

比如从南向北以及对向直行绿灯时间到了转为红灯,那么同一时间从南向西以及对向左转红灯时间到转为绿灯,绿灯时间到转为红灯之后,在同一个时间从西向东以及对向直行红灯时间到转为绿灯,绿灯时间到转为红灯如此往复循环。所以红绿灯需要一个控制器来控制,我们在编程时就单独用一个类来实现这个控制器。由于一个线路上的灯与其对向线路的灯规则完全一致,并且右转绿灯常亮。根据这一现象,我们只需要控制四个方向的灯即可。

综合上面的分析,我们可以大致明白要实现哪些类了,分别为:

1,Road类,代表行驶路线的类,由于这里每条线路都会随机生成一些车辆,所以我们就不需要单独列出一个表示车辆的类了,直接作为路线类的成员变量即可。

2,Lamp类,表示每条线路上的红绿灯的类,由于这里只有固定的12条线路,不能再单独列出一个线路,所以使用枚举定义该类最为合适。

3,LampController类,控制Lamp对象。

4,一个运行程序的主类。

下面就对各个类进行具体分析需要实现哪些功能吧。

 

Road类,

1, 每条线路要有表示自己的名称的属性,自然选择String

2, 每条线程需要一个可以添加、删除(分别对应车辆开到路口、通过路口之后开走)的表表示该线路上的车辆,自然选择ArrayList

3, 每隔2-10秒钟开来一辆车,需要一个单独的线程通过定时器实现。开来一辆车则装入ArrayList列表中,开走一辆车则从列表中删除。车辆使用线路名_ID标识。

4, 检查对应灯的状态,若为绿灯则每一秒钟开走一辆车,从ArrayList列表中删除,同样需要建立一个单独的线程。

[java]  view plain copy
  1. <span style="font-size:14px">import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.Random;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. import java.util.concurrent.ScheduledExecutorService;  
  7. import java.util.concurrent.TimeUnit;  
  8.   
  9.   
  10. public class Road {  
  11.     // 表示车辆的集合  
  12.     private List<String> vechicles = new ArrayList<String>();  
  13.     // 路线名  
  14.     private String name =null;  
  15.       
  16.     public Road(String name){  
  17.         this.name = name;  
  18.           
  19.         //在构造方法中启动一个定时器,每隔一秒检查该方向上的灯是否为绿,  
  20.         //是则打印车辆集合和将集合中的第一辆车移除掉。  
  21.         //创建线程池,使用单个worker线程的Executor,以无界队列方式来运行该线程。  
  22.         ExecutorService pool = Executors.newSingleThreadExecutor();  
  23.           
  24.         //新建一个Runnable的实现类的对象,在未来某个时间执行给定的命令。  
  25.         pool.execute(new Runnable() {  
  26.             public void run(){  
  27.                 int id = 1;  
  28.                 while(true) {  
  29.                     //每隔2-10秒钟驶来一辆车,利用线程休眠实现,新来的车添加进集合末尾,表示后来  
  30.                     try {  
  31.                         Thread.sleep((new Random().nextInt(9) + 2) * 1000);  
  32.                     } catch(InterruptedException e) {  
  33.                         e.printStackTrace();  
  34.                     }  
  35.                     vechicles.add(Road.this.name + "_" + id++);  
  36.                 }  
  37.             }  
  38.         });  
  39.           
  40.         //每隔一秒钟检查路上的灯是否为绿灯,若是,则通过一辆最前面的车,将车从集合中删除  
  41.         ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);  
  42.         timer.scheduleAtFixedRate(  
  43.             new Runnable(){  
  44.                 public void run(){  
  45.                     if(vechicles.size()>0){  
  46.                         boolean lighted = Lamp.valueOf(Road.this.name).isLighted();  
  47.                         if(lighted){  
  48.                             System.out.println(vechicles.remove(0) + " 驶过路口!");  
  49.                         }  
  50.                     }         
  51.                 }  
  52.             },                  //要执行的任务  
  53.             1,                  //首次执行的延迟时间  
  54.             1,                  //两次操作之间的时间差  
  55.             TimeUnit.SECONDS    //设定时间的单位为秒  
  56.         );  
  57.     }  
  58. }</span>  

 

Lamp类,

1,固定十二条线路所以有对应的十二个Lamp实例,上面分析得到只需要控制其中的四条线路即可。

2,每个灯都有状态显示,绿(true)和红(false),并且一条线路上的灯变红或者变绿之后其对向线路上的灯要与之同步,下一条线路上的灯也要随之作出反应变为绿灯。

3,由于直行和左转绿灯时间不同,所以还需要引入方向(Direction)接口并增加一个属性direction表示灯所在路线的方向

[java]  view plain copy
  1. <span style="font-size:14px">public interface Direction {  
  2.     /*表示路线的行驶方法,有三个值,如下*/  
  3.     public static final int LEFT = -1;  
  4.     public static final int CENTER = 0;  
  5.     public static final int RIGHT = 1;  
  6.       
  7.     int getDirection();  
  8. }</span>  
[java]  view plain copy
  1. <span style="font-size:14px">import java.util.Date;  
  2.   
  3. enum Lamp implements Direction {  
  4.     //每个枚举元素各表示一个方向的控制灯  
  5.     S2N("N2S""S2W", CENTER, false), S2W("N2E""E2W", LEFT, false), E2W("W2E""E2S", CENTER, false), E2S("W2N""S2N", LEFT, false),  
  6.       
  7.     // 与上面声明的四个方向的灯一一对应,被动执行即可  
  8.     N2S(nullnull, CENTER, false), N2E(nullnull, LEFT, false), W2E(nullnull, CENTER, false), W2N(nullnull, LEFT, false),  
  9.       
  10.     // 右转信号灯常亮为绿灯  
  11.     S2E(nullnull, RIGHT, true), E2N(nullnull, RIGHT, true), N2W(nullnull, RIGHT, true), W2S(nullnull, RIGHT, true);  
  12.       
  13.     private String opposite;    //表示对向路线的灯  
  14.     private String next;        //表示下一条路线的灯  
  15.     private boolean lighted;    //灯的状态  
  16.     private int direction;      //表示灯所在路线的方向,可能为LEFT\CENTER\RIGHT  
  17.   
  18.       
  19.     private Lamp(String opposite, String next,int direction, boolean lighted){  
  20.         this.opposite = opposite;  
  21.         this.next = next;  
  22.         this.direction = direction;  
  23.         this.lighted = lighted;  
  24.     }  
  25.       
  26.     public boolean isLighted(){   
  27.         return lighted;         //判断灯的状态,变亮表示绿灯,变黑表示红灯  
  28.     }  
  29.       
  30.     public int getDirection() {     //获取灯所在路线的方向  
  31.         return direction;  
  32.     }  
  33.       
  34.     // 将当前灯变为绿灯,同时其对向的灯一起随之改变  
  35.     public void light(){  
  36.         this.lighted = true;  
  37.         if(opposite != null){  
  38.             Lamp.valueOf(opposite).light();  
  39.         }  
  40.         System.out.println(name() + " 绿灯亮了,下面总共应该有6个方向能看到汽车穿过!");  
  41.     }  
  42.       
  43.     // 将当前灯和对应方向的灯变成红灯,并且下一条路线上的灯变绿,返回这个变绿的灯  
  44.     public Lamp blackOut(){  
  45.         this.lighted = false;  
  46.         if(opposite != null){  
  47.             Lamp.valueOf(opposite).blackOut();  
  48.         }         
  49.           
  50.         Lamp nextLamp = null;  
  51.         if(next != null){  
  52.             nextLamp = Lamp.valueOf(next);  
  53.             nextLamp.light();  
  54.             System.out.println("绿灯从" + name() + "-------->切换为" + next);  
  55.             System.out.println("Time :" + new Date());  
  56.         }  
  57.         return nextLamp;  
  58.     }  
  59. }  
  60. </span>  

LampController类,

1, 整个系统中只需要一个交通灯控制器即可,所以,这个类应该设计为单例。

2, 运行时需要指定第一个为绿的灯,所以在构造方法中指定最好。

3, 实现两个定时器分别控制直行和左转的灯,直行绿灯时间20秒,左转绿灯时间15

[java]  view plain copy
  1. <span style="font-size:14px">import java.util.concurrent.Executors;  
  2. import java.util.concurrent.ScheduledExecutorService;  
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5. class LampController {  
  6.     //表示当前灯  
  7.     private Lamp currentLamp;  
  8.     public LampController(){  
  9.         //指定由南向北的灯首先为绿灯  
  10.         currentLamp = Lamp.S2N;  
  11.         currentLamp.light();  
  12.         /*表示首次运行的延迟时间*/  
  13.         int festTime;  
  14.         if(currentLamp.getDirection() == Direction.CENTER) {  
  15.             festTime = 20;  
  16.         } else {  
  17.             festTime = 15;  
  18.         }  
  19.   
  20.         ScheduledExecutorService timer =  Executors.newScheduledThreadPool(1);  
  21.         /*创建一个定时器,如果当前灯所在路线为直行路线则亮20秒绿灯,若是左转路线则为15秒*/  
  22.         timer.scheduleWithFixedDelay(  
  23.             new Runnable() {  
  24.                 public void run() {  
  25.                     currentLamp = currentLamp.blackOut();  
  26.                     /*如果当前灯是直行路线上的灯则增加5秒钟绿灯时间*/  
  27.                     if(currentLamp.getDirection() == Direction.CENTER) {  
  28.                         try {  
  29.                             Thread.sleep(5 * 1000);  
  30.                         } catch(InterruptedException e) {  
  31.                             e.printStackTrace();  
  32.                         }  
  33.                     }  
  34.                 }  
  35.             },  
  36.             festTime,               //首次执行延时  
  37.             15,                 //执行周期  
  38.             TimeUnit.SECONDS        //指定时间单位为秒  
  39.         );  
  40.     }  
  41. }</span>  

主类(TrafficDemo)运行程序

[java]  view plain copy
  1. <span style="font-size:14px">class TrafficDemo {  
  2.     public static void main(String[] args) {  
  3.         String[] directions = new String[]{  
  4.             "S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};  
  5.         // 创建12条路线  
  6.         for(int i=0;i<directions.length;i++){  
  7.             new Road(directions[i]);  
  8.         }  
  9.         // 交通灯控制器  
  10.         new LampController();  
  11.     }  
  12. }</span>  

 

 

这篇关于经典面试项目--交通灯管理系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065922

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

2. 下载rknn-toolkit2项目

官网链接: https://github.com/airockchip/rknn-toolkit2 安装好git:[[1. Git的安装]] 下载项目: git clone https://github.com/airockchip/rknn-toolkit2.git 或者直接去github下载压缩文件,解压即可。