黑馬程式設計師------面試題------交通燈管理系統
------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">java培訓</a>、期待與您交流! ----------
一、模擬實現十字路口的交通燈管理系統邏輯
具體需求如下:
非同步隨機生成按照各個路線行駛的車輛。
例如:
由南向而來去往北向的車輛 ---- 直行車輛
由西向而來去往南向的車輛 ---- 右轉車輛
由東向而來去往南向的車輛 ---- 左轉車輛
。。。
訊號燈忽略黃燈,只考慮紅燈和綠燈。
應考慮左轉車輛控制訊號燈,右轉車輛不受訊號燈控制。
具體訊號燈控制邏輯與現實生活中普通交通燈控制邏輯相同,不考慮特殊情況下的控制邏輯。
注:南北向車輛與東西向車輛交替放行,同方向等待車輛應先放行直行車輛而後放行左轉車輛。
每輛車通過路口時間為1秒(提示:可通過執行緒Sleep的方式模擬)。
隨機生成車輛時間間隔以及紅綠燈交換時間間隔自定,可以設定。
不要求實現GUI,只考慮系統邏輯實現,可通過Log方式展現程式執行結果。
二、分析:
Road類表示路線,總共有12條路線,即系統中總共要產生12個Road例項物件。
每條路線上隨機增加新的車輛,增加到一個集合中儲存。
為了統一程式設計模型,可以假設每條路線都有一個紅綠燈對其進行控制。
其中在4個右拐彎一直可以通車,所以燈為常綠。
其他8條線路兩兩成對,可分4組。
所以,程式只需考慮圖中標註了數字號的4條路線的控制燈的切換順序,這4條路線相反方向的路線的控制燈跟隨這4條路線切換,不必額外考慮。
即,路線1行駛完成後路線2行駛,依次路線3、4,然後迴圈 路線1、2、3、4。
Lamp類來表示一個交通燈,每個交通燈都維護一個狀態:亮(綠)或不亮(紅),每個交通燈要有變亮和變黑的方法,並且能返回自己的亮黑狀態。
總共有12條路線,所以,系統中總共要產生12個交通燈。右拐彎的路線本來不受燈的控制,設定這些燈為常亮狀態,即永遠不變黑。
另外8個燈,它們是兩兩成對的,可以歸為4組,
所以,只要從這4組中各取出一個燈,對這4個燈依次輪詢變亮,與這4個燈方向對應的燈則隨之一同變化,
因此Lamp類中要有一個變數來記住自己相反方向的燈,在一個Lamp物件的變亮和變黑方法中,將對應方向的燈也變亮和變黑。每個燈變黑時,都伴隨者下一個燈的變亮,
Lamp類中還用一個變數來記住自己的下一個燈。
無論在程式的什麼地方去獲得某個方向的燈時,每次獲得的都是同一個例項物件,所以Lamp類改用列舉來做顯然具有很大的方便性,永遠都只有代表
12個方向的燈的例項物件。
三、程式碼實現
1)Road類
每個Road物件都有一個name成員變數來代表方向,有一個vehicles成員變數來代表方向上的車輛集合。
在Road物件的構造方法中啟動一個執行緒每隔一個隨機的時間向vehicles集合中增加一輛車(用一個“路線名_id”形式的字串進行表示)。
在Road物件的構造方法中啟動一個定時器,每隔一秒檢查該方向上的燈是否為綠,是則列印車輛集合和將集合中的第一輛車移除掉。
- 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() {
- publicvoid run() {
- for (int i = 1; i < 1000; i++) {
- try {
- Thread.sleep((new Random().nextInt(10) + 1) * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- vechicles.add(Road.this.name + "_" + i);
- }
- }
- });
- // 每隔一秒檢查對應的燈是否為綠,是則放行一輛車
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(new Runnable() {
- publicvoid run() {
- if (vechicles.size() > 0) {
- boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
- if (lighted) {
- System.out.println(vechicles.remove(0)
- + " is traversing !");
- }
- }
- }
- }, 1, 1, TimeUnit.SECONDS);
- }
- }
2)Lamp類
系統中有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:14px">publicenum 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;
- }
- /* 當前燈是否為綠 */
- privateboolean lighted;
- /* 與當前燈同時為綠的對應方向 */
- private String opposite;
- /* 當前燈變紅時下一個變綠的燈 */
- private String next;
- publicboolean isLighted() {
- return lighted;
- }
- /**
- * 某個燈變綠時,它對應方向的燈也要變綠
- */
- publicvoid light() {
- this.lighted = true;
- if (opposite != null) {
- Lamp.valueOf(opposite).light();
- }
- System.out.println(name() + " lamp is green,下面總共應該有6個方向能看到汽車穿過!");
- }
- /**
- * 某個燈變紅時,對應方向的燈也要變紅,並且下一個方向的燈要變綠
- *
- * @return 下一個要變綠的燈
- */
- 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;
- }
- }</span>
3)LampController類
整個系統中只能有一套交通燈控制系統,所以,LampController類最好是設計成單例。
LampController構造方法中要設定第一個為綠的燈。
LampController物件的start方法中將當前燈變綠,然後啟動一個定時器,每隔10秒將當前燈變紅和將下一個燈變綠。
- <span style="font-size:14px">publicclass LampController {
- private Lamp currentLamp;
- public LampController() {
- // 剛開始讓由南向北的燈變綠;
- currentLamp = Lamp.S2N;
- currentLamp.light();
- /* 每隔10秒將當前綠燈變為紅燈,並讓下一個方向的燈變綠 */
- ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(new Runnable() {
- publicvoid run() {
- currentLamp = currentLamp.blackOut();
- }
- }, 10, 10, TimeUnit.SECONDS);
- }
- }</span>
4)MainClass類
用for迴圈創建出代表12條路線的物件。
接著再獲得LampController物件並呼叫其start方法。
- publicclass MainClass {
- /**
- * @param args
- */
- publicstaticvoid main(String[] args) {
- /* 產生12個方向的路線 */
- String[] directions = new String[] { "S2N", "S2W", "E2W", "E2S", "N2S",
- "N2E", "W2E", "W2N", "S2E", "E2N", "N2W", "W2S" };
- for (int i = <