黑馬程式設計師_面試題1(交通燈管理系統)
一.專案需求:
模擬實現十字路口的交通燈管理系統邏輯:非同步隨機生成按照各個路線行駛的車輛。
例如:
由南向而來去往北向的車輛 ---- 直行車輛
由西向而來去往南向的車輛 ---- 右轉車輛
由東向而來去往南向的車輛 ---- 左轉車輛
。。。
訊號燈忽略黃燈,只考慮紅燈和綠燈。
應考慮左轉車輛控制訊號燈,右轉車輛不受訊號燈控制。
具體訊號燈控制邏輯與現實生活中普通交通燈控制邏輯相同,不考慮特殊情況下的控制邏輯。
注:南北向車輛與東西向車輛交替放行,同方向等待車輛應先放行直行車輛而後放行左轉車輛。
每輛車通過路口時間為1秒(提示:可通過執行緒Sleep的方式模擬)。
隨機生成車輛時間間隔以及紅綠燈交換時間間隔自定,可以設定。
二.開發前分析:
1. 一個方向到其他方向有3條線路,4個方向共12條線路,為了統一程式設計模型,
可以假設每條路都有一個紅綠燈對其進行控制, 右轉向的4個路口的控制燈可以假設為常亮綠燈狀態。
2.為了思路清晰,先思考一個方向的路線問題。
3.初步設想所擁有的物件:紅綠燈,紅綠燈的控制系統,(汽車),路線。
4.汽車要看所線上路上的紅綠燈,並且判斷前方是否有車。路中儲存著汽車的集合,顯然路上就應該有增加車輛 和減少車輛的方法了。
我們要捕捉路上減少一輛車的過程,所以,這個車並不需要單獨設計成為一個物件。用一個字串表示就行了。
三.開發過程
類的編寫:
Road類的編寫:
每個Road物件都有一個name成員變數來代表方向,有一個vehicles成員變數來代表方向上的車輛集合。
在Road物件的構造方法中啟動一個執行緒每隔一個隨機的時間向vehicles集合中增加一輛車(用一個“路線名_id”形 式的字串進行表示)。
在Road物件的構造方法中啟動一個定時器,每隔一秒檢查該方向上的燈是否為綠,是則列印車輛集合和將集合 中的第一輛車移除掉。
程式碼如下:
- <span style=
"font-size:18px;">- publicclass Road {
- private List<String> vechicles = new ArrayList<String>();
- private String name = null;
- public Road(String name) {
- this.name = name;
- // 建立車輛
- ExecutorService pool = Executors.newSingleThreadExecutor(); //執行緒池
- pool.execute(new Runnable() {
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- for (int i = 1; i < 1000; i++) {
- try {
- // 隨機獲取1-10秒的數,讓車輛不定時的往裡增加
- Thread.sleep((new Random().nextInt(10) + 1) * 1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- vechicles.add(Road.this.name + "_" + i);
- }
- }
- });
- //建立一個執行緒,按時間安排執行任務的功能
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(new Runnable() {
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- if (vechicles.size() > 0) // 判斷現在是否有車輛
- {
- boolean lighted = Lamp.valueOf(Road.this.name).isLight();
- //System.out.println(Road.this.name+">>>>>>>>>>>>>"+lighted);
- if (lighted) {
- System.out
- .println(vechicles.remove(0) + " ...is traverse");
- }
- }
- }
- }, 1, 1, TimeUnit.SECONDS);
- }
- }</span>
Lamp燈的編寫:
Lamp類來表示一個交通燈,12個,使用列舉。
系統中有12個方向上的燈,在程式的其他地方要根據燈的名稱就可以獲得對應
的燈的例項物件,綜合這些因素,將Lamp類用java5中的列舉形式定義更為簡單。
每個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進入死迴圈。
程式碼如下:
- <span style="font-size:18px;">
- publicclass Road {
- private List<String> vechicles = new ArrayList<String>();
- private String name = null;
- public Road(String name) {
- this.name = name;
- // 建立車輛
- ExecutorService pool = Executors.newSingleThreadExecutor(); //執行緒池
- pool.execute(new Runnable() {
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- for (int i = 1; i < 1000; i++) {
- try {
- // 隨機獲取1-10秒的數,讓車輛不定時的往裡增加
- Thread.sleep((new Random().nextInt(10) + 1) * 1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- vechicles.add(Road.this.name + "_" + i);
- }
- }
- });
- //建立一個執行緒,按時間安排執行任務的功能
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(new Runnable() {
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- if (vechicles.size() > 0) // 判斷現在是否有車輛
- {
- boolean lighted = Lamp.valueOf(Road.this.name).isLight();
- //System.out.println(Road.this.name+">>>>>>>>>>>>>"+lighted);
- if (lighted) {
- System.out
- .println(vechicles.remove(0) + " ...is traverse");
- }
- }
- }
- }, 1, 1, TimeUnit.SECONDS);
- }
- }</span>
LampController類的編寫:
整個系統中只能有一套交通燈控制系統,所以,LampController類最好是設計成單例。
LampController構造方法中要設定第一個為綠的燈。
LampController物件的start方法中將當前燈變綠,然後啟動一個定時器,每隔10秒將當前燈變紅和將下一個燈變 綠。
- <span style="font-size:18px;"> 程式碼如下:
- publicclass LampController {
- private Lamp controlLamp;
- public LampController() {
- controlLamp = Lamp.S2N; //先初始化一條線路上的燈
- controlLamp.light();
- // TODO Auto-generated constructor stub
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleWithFixedDelay(
- new Runnable() {
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- controlLamp = controlLamp.blackOut();
- }
- },
- 10,
- 10,
- TimeUnit.SECONDS);
- }
- }
- MainClass類的編寫
- 用for迴圈創建出代表12條路線的物件。
- 接著再獲得LampController物件並呼叫其start方法。
- 程式碼如下:
- publicclass MainClass {
- /**
- * @param args
- */
- publicstaticvoid 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();
- }
- }</span>
注:面向物件的思路要清晰:誰擁有資料,誰就提供這些資料的方法。例子:人開門,人在黑板上畫圓, 司機剎車。