1. 程式人生 > >黑馬程式設計師 7K面試題破解:交通燈管理系統

黑馬程式設計師 7K面試題破解:交通燈管理系統

---------------------- ASP.Net+Android+IOS開發.Net培訓、期待與您交流! ----------------------

交通燈管理系統

使用者需求

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

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

例如:

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

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

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

       。。。

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

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

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

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

        每輛車通過路口時間為1秒(提示:可通過執行緒Sleep的方式模擬)。

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

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


需求分析

圖解說明


文字說明

(1)這裡總共有12條路線,每條路線作為一個物件存在。

(2)作為程式設計模型,可假設每條線路都有一個紅綠燈,對其控制。

(3)其中右轉彎的4條路線的控制燈可以假設為常綠狀態。

(4)另外的八條線路是兩兩成對的,就可以歸為4組,程式只需考慮圖中標註了數字的4條路線的控制燈的切換順序即可,這4條路線的對應的反向的控制燈是隨著這4條路線進行相同切換的。


概要設計

涉及到的物件:紅綠燈、紅綠燈控制系統、汽車和路線。

1、對路線和車兩個物件的分析:

路擁有車輛這個資料,路本身知道自己身上有幾輛車,所以,路作為一個集合,有增加和刪除物件(車)的方法,這樣就可以將車和路線繫結在一起。

1)設計一個Road類表示路線,有12條路線,就產生12個Road物件。|

2)路里面有一個集合,可以隨機增加新的車輛存入集合中,這個集合可以定時的刪除車輛。

3)每條路線每隔一秒都會對控制本路線的燈是否為綠,綠燈狀態就會將車存入集合;並每隔一秒移除第一輛車,表示車輛穿過路口。

2、對紅綠燈和紅綠燈控制系統兩個物件的分析:

1)設計一個Lamp類來表示一個交通燈,每個交通燈都維護一個狀態:亮(綠)或不亮(紅),每個交通燈要有變亮和變黑的方法,並且能返回自己的亮黑狀態。

2)對應12條路線要產生12個交通燈。右拐彎的路線可不受燈的控制,但是為了讓程式採用統一的處理方式,故假設出有四個右拐彎的燈且為常亮狀態,即永遠不變黑。

3)除了右拐彎方向的其他8條路線的燈,它們是兩兩成對的,可以歸為4組,所以,在程式設計處理時,只要從這4組中各取出一個燈,對這4個燈依次輪詢變亮,與這4個燈方向對應的燈則隨之一同變化,因此Lamp類中要有一個變數來記住自己相反方向的燈,在一個Lamp物件的變亮和變黑方法中,將對應方向的燈也變亮和變黑。每個燈變黑時,都伴隨者下一個燈的變亮,Lamp類中還用一個變數來記住自己的下一個燈。

4)無論在程式的什麼地方去獲得某個方向的燈時,每次獲得的都是同一個例項物件,所以Lamp類改用列舉來做顯然具有很大的方便性,永遠都只有代表12個方向的燈的例項物件。

5)設計一個LampController類,它定時讓當前的綠燈變紅。


詳細設計

Road類的編寫

分析:

1、可以將每條路線看作一個物件,就有12個Road的例項物件。

2、每條路線上都有有車,應該隨機產生新的車輛,將產生的車輛儲存到路內部的一個集合中。在Road物件的構造方法中啟動一個執行緒每隔一個隨機的時間向vehicles集合中增加一輛車(用一個“路線名_id”形式的字串進行表示)。

3、路要以燈為標準,判斷是否燈是亮的,在燈亮的時間內,才能讓車行駛,即移除路面上的第一輛車。

4、移除車輛的操作,要用到定時器,並每隔一秒檢查該方向上燈是否為綠,是則在相應的時間內移除第一輛車,使用scheduleAtFixedRate方法。


Lamp類的編寫

分析:

1、總共有12條路線,有12個方向,就需要定義12燈,用列舉類建立燈物件

2、12個燈分別如程式碼中所示

3、12個燈中,有四個右轉的等可設為常亮狀態,即S2E,E2N,N2W,W2S

4、對於剩下的燈,可分為4組,每組的兩個燈是對應的,這裡設定當前燈和對應燈,

5、對應的兩個燈是同時亮的,而與其垂直方向的燈則不能亮。如南北方向燈亮,東西方向則不亮

6、當前燈亮時,就要設定對應燈為亮,並設定垂直方向燈為不亮


LampContriller類的編寫

分析:交通燈控制器

1、最開始有個當前燈作為第一個燈執行

2、在構造方法中,

       要指定當前燈為綠狀態,以S2N作為第一個

       建立一個定時器,每隔十秒,讓當前燈變紅,讓下一個方向(垂直方向)的燈變綠


測試類的編寫

測試:

1、建立12條路線,即12個物件

2、執行交通燈控制器系統LampController

擴充套件工具類

本類純屬自己擴充:

因為本人英語不是很好,看程式各個線路和執行結果各個線路很是費時,所以為了程式結果在這具有更好的可讀性,編寫此類。

SwitchLamp工具類,提供一個方法,把燈,路的英文縮寫,轉換成中文。

編碼

Road類----------路類

/*
 * 每個Road物件代表一條路線,總共有12條路線,即系統中總共要產生12個Road例項物件。
 * 每條路線上隨機增加新的車輛,增加到一個集合中儲存。
 * 每條路線每隔一秒都會檢查控制本路線的燈是否為綠,是則將本路線儲存車的集合中的第一輛車移除,即表示車穿過了路口。
 */
public class Road {
	// 車輛集合
	private List<String> vechicles = new ArrayList<String>();

	// 每條路線的名字
	private String name = null;

	public Road(final String name) {
		this.name = name;

		// 模擬車輛不斷隨機上路的過程
		// public static ExecutorService newSingleThreadExecutor()
		// 建立一個使用單個 worker 執行緒的 Executor,以無界佇列方式來執行該執行緒。
		ExecutorService pool = Executors.newSingleThreadExecutor();
		pool.execute(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i < 1000; i++) {
					try {
						// 讓執行緒睡眠,時間隨機生成為1-10秒
						Thread.sleep((new Random().nextInt(10) + 1) * 1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					//vechicles.add(Road.this.name + "_" + i);
					//呼叫SwitchLamp類的靜態方法convert()把英文名改成中文名
					vechicles.add(SwitchLamp.convert(Road.this.name) + "_" + i);
				}
			}
		});

		// 每隔一秒檢查對應的燈是否為綠,是則放行一輛車
		// public static ScheduledExecutorService newScheduledThreadPool(int
		// corePoolSize)
		// 建立一個執行緒池,它可安排在給定延遲後執行命令或者定期地執行。
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);

		// ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
		// 建立並執行一個在給定初始延遲後首次啟用的定期操作,後續操作具有給定的週期
		// command---要執行的執行緒
		// initialDelay---首次執行的延遲時間
		// period---連續執行之間的週期,多少單位以後再執行
		// unit---initialDelay和period引數的時間單位,毫秒,秒,分還是什麼?
		timer.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				if (vechicles.size() > 0) {
					//獲取這條路線的燈物件,再判斷這個燈是否為綠色
					//內部類要用到外部類的成員變數外部類名.this.成員變數名
					boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
					//boolean lighted = Lamp.valueOf(SwitchLamp.convert(Road.this.name)).isLighted();
					//假如綠燈是亮的,則放行一輛車,把這個車在集合中刪除,並輸出
					if(lighted){
						System.out.println(vechicles.remove(0)+"車通過!");
					}
				}
			}
		}, 
		1,// 1單位後開始執行
		1,// 每過1單位後執行一次
		TimeUnit.SECONDS);// 單位:秒
	}
}


Lamp類----------交通燈類

/**
 * Lamp 燈類
 * 
 * 每個Lamp元素代表一個方向的燈,總共有12個方向,所以總共有12個Lamp元素。
 * 以下字母代表方向:
 * 		N------North------北
 * 		S------South------南
 * 		W------West-------西
 * 		E------East-------東
 * 
 * 有如下一些方向的燈,每兩個形成一組,一組燈同時變綠或變紅,所以,
 * 程式程式碼只需要控制每組燈中的一個燈即可:
 * 
 * S2N,N2S------南到北,北到南
 * S2W,N2E------南到西,北到東
 * E2W,W2E------東到西,西到東
 * E2S,W2N------東到南,西到北
 * S2E,N2W------南到東,北到西
 * E2N,W2S------東到北,西到南
 * 上面最後兩行的燈是虛擬的,由於從南到東和從西向北,以及它們的對應方向不受紅綠燈的控制,
 * 所以,可以假想它們總是綠燈。
 *
 * @author 小小灬程式猿
 */
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);

	private Lamp() {
	}

	// 構造方法:於當前燈同時為綠的方向,下一個變綠的燈,當前燈是否為綠
	private Lamp(String opposite, String next, boolean lighted) {
		this.opposite = opposite;
		this.next = next;
		this.lighted = lighted;
	}

	// 與當前燈同時為綠的對應方向
	private String opposite;
	// 當前燈變紅時下一個變綠的燈
	private String next;
	// 當前燈是否為綠
	private boolean lighted;

	/** 
	 * 判斷當前燈是否變綠
	 */
	public boolean isLighted() {
		return lighted;
	}
	
	/**
	 * 把某個燈變綠時,它對應方向的燈也要變綠
	 */
	public void light(){
		//把某個變綠
		this.lighted=true;
		//對應的燈如果不為空,也要變綠
		if(opposite !=null){
			Lamp.valueOf(opposite).light();
		}
		//呼叫SwitchLamp類的靜態方法convert()把英文名改成中文名
		System.out.println(SwitchLamp.convert(name())+"路交通燈變綠!");
	}
	
	/**
	 * 某個燈變紅時,對應方向的燈也要變紅,並且下一個方向的燈要變綠
	 * @return 下一個要變綠的燈
	 */
	public Lamp blackOut(){
		//把當前燈變紅
		this.lighted=false;
		//如果對應的燈不是空的話,也把它的燈變紅
		if(opposite!=null){
			Lamp.valueOf(opposite).blackOut();
		}
		
		//下一個要變綠的燈
		Lamp nextLamp = null;
		//下一個燈如果不為空的話
		if(next != null){
			//先獲取下一個燈的物件
			nextLamp = Lamp.valueOf(next);
			//列印到控制檯
			//呼叫SwitchLamp類的靜態方法convert()把英文名改成中文名
			System.out.println("綠燈從"+SwitchLamp.convert(name())+"-------->切換為" + SwitchLamp.convert(next));
			//把下一個燈變綠
			nextLamp.light();
		}
		return nextLamp;
	}
}


LampContriller類---------交通燈控制系統類

/**
 * 交通燈控制系統
 * @author 小小灬程式猿
 *
 */
public class LampController {
	private Lamp currentLamp;
	
	public LampController(){
		//剛開始讓由南向北的燈變綠
		//獲取到由南向北的燈物件
		currentLamp=Lamp.S2N;
		//把這個燈變綠
		currentLamp.light();
		
		//每隔10秒將當前綠燈變為紅燈,並讓下一個方向的燈變綠
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		timer.scheduleAtFixedRate(
				new Runnable(){
					@Override
					public void run() {
						currentLamp =currentLamp.blackOut();
					}
				}, 
				10,
				10,
				TimeUnit.SECONDS);
	}
}


SwitchLamp類-----------工具類

/**
 * 工具類,用來把傳過來哪路到哪路的英文,變為中文
 * 
 * @author 小小灬程式猿
 * 
 */
public class SwitchLamp {

	// 因為工具類,所以構造方法私有
	private SwitchLamp() {
	}

	/**
	 * 根據傳來的路的英文名字,返回路的中文名字
	 * 
	 * @param 路英文名字
	 * @return 路中文名字
	 */
	public static String convert(String name) {
		// 1.7新特性switch可以是字串
		// 由於我的myeclipse設定不了編譯器7.0所以只能用下面的,建議使用switch
		// switch(name){
		// }
		String temp = null;
		if (name.equals("S2N")) {
			temp = "南到北";
		} else if (name.equals("S2W")) {
			temp = "南到西";
		} else if (name.equals("S2E")) {
			temp = "南到東";
		} else if (name.equals("N2S")) {
			temp = "北到南";
		} else if (name.equals("N2E")) {
			temp = "北到東";
		} else if (name.equals("N2W")) {
			temp = "北到西";
		} else if (name.equals("E2W")) {
			temp = "東到西";
		} else if (name.equals("E2S")) {
			temp = "東到南";
		} else if (name.equals("E2N")) {
			temp = "東到北";
		} else if (name.equals("W2E")) {
			temp = "西到東";
		} else if (name.equals("W2N")) {
			temp = "西到北";
		} else if (name.equals("W2S")) {
			temp = "西到南";
		}
		return temp;
	}
}


MainClass類-----------測試類

public class MainClass {
	public static void main(String[] args) {
		//產生12個方向的路線
		String[] directions = new String[]{
			"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"	
		};
		for(String road : directions){
			new Road(road);
		}
		//啟動整個交通燈系統
		new LampController();
	}
}


測試:

圖示:


說明 :

每次一個燈變綠,對應的一個燈也變綠,還有四個向右轉彎的燈,所以每次最多有6個路的車通過,

可以少於6個,因為這條路可能沒有車,通過檢視,比較,發現沒有錯誤。

---------------------- ASP.Net+Android+IOS開發.Net培訓、期待與您交流! ----------------------