1. 程式人生 > >黑馬程式設計師-面試題之交通燈

黑馬程式設計師-面試題之交通燈

題目:交通燈管理系統

模擬實現十字路口的交通燈管理系統邏輯,具體需求如下:

Ø 非同步隨機生成按照各個路線行駛的車輛。

例如:

       由南向而來去往北向的車輛 ---- 直行車輛

       由西向而來去往南向的車輛 ---- 右轉車輛

       由東向而來去往南向的車輛 ---- 左轉車輛

       。。。

Ø 訊號燈忽略黃燈,只考慮紅燈和綠燈。

Ø 應考慮左轉車輛控制訊號燈,右轉車輛不受訊號燈控制。

Ø 具體訊號燈控制邏輯與現實生活中普通交通燈控制邏輯相同,不考慮特殊情況下的控制邏輯。

注:南北向車輛與東西向車輛交替放行,同方向等待車輛應先放行直行車輛而後放行左轉車輛。

Ø 每輛車通過路口時間為1

秒(提示:可通過執行緒Sleep的方式模擬)。

Ø 隨機生成車輛時間間隔以及紅綠燈交換時間間隔自定,可以設定。

Ø 不要求實現GUI,只考慮系統邏輯實現,可通過Log方式展現程式執行結果。

程式碼實現:
package along.study.traffic;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//每個列舉變數代表一條路線上的紅綠燈,總共有12條路線,也就有12個列舉變數
public enum Lamp {
	S2N("N2S", "S2L", false), S2L("N2L", "W2E", false), W2E("E2W", "W2L", false), W2L(
			"E2L", "S2N", false), N2S(null, null, false), N2L(null, null, false), E2W(
			null, null, false), E2L(null, null, false), S2R(null, null, true), W2R(
			null, null, true), N2R(null, null, true), E2R(null, null, true);

	private boolean lighted = false;//代表燈的當前顏色狀態

	public boolean isLighted() {
		return lighted;
	}

	private String oppsite = null;//代表燈對面的燈
	private String next = null;//代表燈的下一個燈
	private static Lamp currentLightedLamp = null;//代表當前顏色為綠色的那個燈

	private Lamp() {
	}

	private Lamp(String oppsite, String next, boolean lighted) {
		this.lighted = lighted;
		this.oppsite = oppsite;
		this.next = next;
	}

	//呼叫此方法,將使當前的燈以及對面的燈(如果有)的顏色變成綠色
	private Lamp turn2Green() {
		this.lighted = true;
		if (oppsite != null) {
			Lamp.valueOf(oppsite).lighted = true;
			System.out.println(this + " and " + Lamp.valueOf(oppsite)
					+ " turn to green");
		}
		return this;
	}

	//呼叫此方法,將使當前的燈以及對面的燈(如果有)的顏色變成紅色
	//並將當前的燈的下一個燈(如果有)變成綠色
	private Lamp turn2Red() {
		this.lighted = false;
		if (oppsite != null) {
			Lamp.valueOf(oppsite).lighted = false;
			System.out.println(this + " and " + Lamp.valueOf(oppsite)
					+ " turn to red");
		}

		Lamp nextLamp = null;
		if (next != null) {
			nextLamp = Lamp.valueOf(next);
			nextLamp.turn2Green();
		}
		return nextLamp;
	}

	/**
	 * 對外的呼叫介面
	 * @param firstLamp 第一個變綠的燈
	 * @param period 每個燈保持綠色的時間
	 */
	public static void running(Lamp firstLamp, int period) {
		currentLightedLamp = firstLamp.turn2Green();
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				currentLightedLamp = currentLightedLamp.turn2Red();
			}
		}, period, period, TimeUnit.SECONDS);
	}

	@Override
	public String toString() {
		return this.name();
	}
}

package along.study.traffic;

import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
//代表路線,並且可以在某個時間段內隨機產生代表汽車的變數,並將其
//儲存到一個汽車集合中
public class Road {
	//可以考慮將Road類做成一個介面,然後使用者來實現介面。這樣
	//一來,Lamp類就可以提前編寫呼叫介面的程式碼,以實現燈轉換,就去主動
	//通知車行走的功能,而不是讓車在那裡一遍一遍不停的詢問燈的狀態,
	//造成CPU的浪費。。。。但是,按照面向物件的思想來考慮,就應該是
	//車去看燈的狀態,而不是燈綠就一定會導致車跑,跑不跑,還是要由車看
	//燈得到的結果來決定,所以還是不要改了
	//儲存車輛的集合應該採用同步集合,以防在刪除動作完成之前又執行了新增動作

	private String roadName;
	private Lamp lamp;
	private CopyOnWriteArrayList<String>vehicles = new CopyOnWriteArrayList<String>();
	private int vehicleGeneratTimeSeed = 10;
	
	public Road(String roadName){
		this.roadName = roadName;
		lamp = Lamp.valueOf(getRoadName());
		
		//生成汽車,並儲存到集合中
		Executors.newSingleThreadExecutor()
		.execute(new Runnable() {
			@Override
			public void run() {
				for(int i = 1;i <= 1000;i++){
					vehicles.add(getRoadName() + "向第" + i + "輛車");
					try {
						//模擬汽車隨機上路的時間點
						Thread.sleep(((long)new Random().nextInt(vehicleGeneratTimeSeed) + 1) * 1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		//檢查當前路線上的紅綠燈狀態,若為綠燈,則移除集合中最開始的汽車
		Executors.newSingleThreadExecutor()
		.execute(new Runnable() {
			@Override
			public void run() {
				while(true){
					if(lamp.isLighted()){
						if(vehicles.size() > 0){
							String removedVehicle = vehicles.remove(0);
							try {
								//模擬汽車穿過路口時所需要的時間
								Thread.sleep(vehicleGeneratTimeSeed);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							System.out.println(removedVehicle + "已經通過路口");
						}else{
//							System.out.println(getRoadName() + "方向上暫時沒有車輛通行");
						}
					}
				}
			}
		});
	}

	private String getRoadName() {
		return roadName;
	}


}

package along.study.traffic;

//測試類
public class MainClass {
	public static void main(String[] args) {
		String[]lampsNames = {"S2N","S2L","W2E","W2L",
				"N2S","N2L","E2W","E2L",
				"S2R","W2R","N2R","E2R"};
		//模擬生成12條路
		for(int i = 0;i < lampsNames.length;i++){
			new Road(lampsNames[i]);
		}
		//呼叫燈的靜態方法,開始工作
		Lamp.running(Lamp.S2N, 10);
	}
}

試題答案是根據張孝祥老師的《交通燈管理系統視訊》講解整理而成,在張老師的視訊中,將紅綠燈狀態的轉換採用一個新類來實現。在我的整理的程式碼中,將這種轉換直接封裝到了燈的對外方法中,這樣一來,燈的具體轉換方式就隱藏起來,不對外暴漏,外界在使用燈的時候,只需要傳遞給對外方法兩個引數即可,一個是第一個要變綠的燈,一個是燈綠的持續時間。我想這樣更能體現面向物件的封裝性

下面是執行結果示例

S2N and N2S turn to green
S2N向第1輛車已經通過路口
W2R向第1輛車已經通過路口
N2S向第1輛車已經通過路口
S2R向第1輛車已經通過路口
N2R向第1輛車已經通過路口
E2R向第1輛車已經通過路口
S2N向第2輛車已經通過路口
S2R向第2輛車已經通過路口
N2S向第2輛車已經通過路口
N2R向第2輛車已經通過路口
S2R向第3輛車已經通過路口
E2R向第2輛車已經通過路口
S2R向第4輛車已經通過路口
W2R向第2輛車已經通過路口
N2S向第3輛車已經通過路口
N2R向第3輛車已經通過路口
S2N and N2S turn to red
S2L and N2L turn to green
S2L向第1輛車已經通過路口
N2L向第1輛車已經通過路口
S2L向第2輛車已經通過路口
N2L向第2輛車已經通過路口
S2L向第3輛車已經通過路口
N2L向第3輛車已經通過路口
E2R向第3輛車已經通過路口
S2L向第4輛車已經通過路口
W2R向第3輛車已經通過路口
E2R向第4輛車已經通過路口
S2L向第5輛車已經通過路口
S2R向第5輛車已經通過路口
N2L向第4輛車已經通過路口
N2R向第4輛車已經通過路口
S2L and N2L turn to red
W2E and E2W turn to green
E2W向第1輛車已經通過路口
W2E向第1輛車已經通過路口
E2W向第2輛車已經通過路口
W2E向第2輛車已經通過路口
E2W向第3輛車已經通過路口
W2E向第3輛車已經通過路口
E2W向第4輛車已經通過路口
W2E向第4輛車已經通過路口
E2W向第5輛車已經通過路口
W2E向第5輛車已經通過路口
W2E向第6輛車已經通過路口
W2E向第7輛車已經通過路口
N2R向第5輛車已經通過路口
W2R向第4輛車已經通過路口
W2E向第8輛車已經通過路口
E2R向第5輛車已經通過路口
E2W向第6輛車已經通過路口
E2R向第6輛車已經通過路口
W2E向第9輛車已經通過路口
S2R向第6輛車已經通過路口
N2R向第6輛車已經通過路口
W2E and E2W turn to red
W2L and E2L turn to green
E2L向第1輛車已經通過路口
W2L向第1輛車已經通過路口
E2L向第2輛車已經通過路口
W2L向第2輛車已經通過路口
E2L向第3輛車已經通過路口
W2L向第3輛車已經通過路口
W2L向第4輛車已經通過路口
E2L向第4輛車已經通過路口
W2L向第5輛車已經通過路口
E2L向第5輛車已經通過路口
W2L向第6輛車已經通過路口
S2R向第7輛車已經通過路口
E2R向第7輛車已經通過路口
W2L向第7輛車已經通過路口
W2R向第5輛車已經通過路口
N2R向第7輛車已經通過路口
E2L向第6輛車已經通過路口
E2R向第8輛車已經通過路口
W2R向第6輛車已經通過路口
E2L向第7輛車已經通過路口
W2L and E2L turn to red
S2N and N2S turn to green
N2S向第4輛車已經通過路口
S2N向第3輛車已經通過路口
S2N向第4輛車已經通過路口
N2S向第5輛車已經通過路口
S2R向第8輛車已經通過路口
S2N向第5輛車已經通過路口
N2S向第6輛車已經通過路口
N2S向第7輛車已經通過路口
S2N向第6輛車已經通過路口
N2S向第8輛車已經通過路口
S2N向第7輛車已經通過路口
N2S向第9輛車已經通過路口
S2N向第8輛車已經通過路口
N2S向第10輛車已經通過路口
N2R向第8輛車已經通過路口
W2R向第7輛車已經通過路口
S2N向第9輛車已經通過路口
S2R向第9輛車已經通過路口
N2S向第11輛車已經通過路口
N2S向第12輛車已經通過路口
E2R向第9輛車已經通過路口
N2S向第13輛車已經通過路口
S2R向第10輛車已經通過路口
N2R向第9輛車已經通過路口
S2N and N2S turn to red
S2L and N2L turn to green
S2L向第6輛車已經通過路口
N2L向第5輛車已經通過路口
S2L向第7輛車已經通過路口
N2L向第6輛車已經通過路口
N2L向第7輛車已經通過路口
S2L向第8輛車已經通過路口
N2L向第8輛車已經通過路口
S2L向第9輛車已經通過路口
N2L向第9輛車已經通過路口
S2L向第10輛車已經通過路口
S2R向第11輛車已經通過路口
S2L向第11輛車已經通過路口
N2L向第10輛車已經通過路口
S2L向第12輛車已經通過路口
N2L向第11輛車已經通過路口
E2R向第10輛車已經通過路口
N2L向第12輛車已經通過路口
W2R向第8輛車已經通過路口
S2L向第13輛車已經通過路口
N2R向第10輛車已經通過路口
N2L向第13輛車已經通過路口
W2R向第9輛車已經通過路口
S2L向第14輛車已經通過路口
W2R向第10輛車已經通過路口
S2L向第15輛車已經通過路口
S2R向第12輛車已經通過路口
S2L and N2L turn to red
W2E and E2W turn to green
W2E向第10輛車已經通過路口
E2W向第7輛車已經通過路口
E2R向第11輛車已經通過路口
W2E向第11輛車已經通過路口
E2W向第8輛車已經通過路口
W2E向第12輛車已經通過路口
E2W向第9輛車已經通過路口
W2E向第13輛車已經通過路口
E2W向第10輛車已經通過路口
E2W向第11輛車已經通過路口
E2W向第12輛車已經通過路口
W2E向第14輛車已經通過路口
E2W向第13輛車已經通過路口