1. 程式人生 > >專案——交通燈管理系統

專案——交通燈管理系統

                                                 交通燈管理系統

一.交通燈管理專案的需求

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

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

例如:

由北向駛往南向的車輛----直行

由西向駛往南向的車輛----右轉

由東向駛往南向的車輛----左轉

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

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

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

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

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

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

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

二.需求分析

總共有12條路線。為了統一程式設計模型,可以假設每條路線都有一個紅綠燈對其進行控制,右轉彎的4條路線的控制燈可以假設為常綠狀態。其他8條路線兩兩成對,可以歸為4組。所以,程式只需要考慮圖中標註了編號的4條路線的控制燈的切換順序,這4條路線的控制燈跟隨這4條路線切換,不必額外考慮。

三.面向物件的分析與設計

1.每條路線上都會出現多輛車,路線上要隨機增加新的車,在綠燈期間還要每秒鐘減少一輛車。

                   1)設計一個Road類來表示路線,每個Road物件代表一條路線,總共12條路線

即系統中總共產生12Road例項物件。

                   2)每條線路上隨機增加新的車輛,增加到一個集合中儲存。

                   3)每條線路每隔1秒都會檢查控制本路線的燈是否為綠。若是綠燈,將本路線儲存

車的集合中的第一輛車移除,即表示車穿過了路口。

2.每條路線每隔1秒都會檢查控制本路線的燈是否為綠。一個燈由綠變紅時,應該將下一個方向的燈變綠。

1) 設計一個Lamp類來表示一個交通燈,每個交通燈都維護一個狀態,亮(綠)或不亮(紅),每個交通燈要有變亮

   和變黑的方法,並且能返回自己亮黑狀態。

2) 總共有12條路線,系統要產生12個交通燈。右轉彎的路線本來不受燈控制,但是為了讓程式有統一的處理方式,

   所以假設四個右轉彎的燈,只是需要將這些燈設定為常亮狀態,永遠都不要變黑。

3) 除了右轉彎方向的其他8條路線的燈,他們是兩兩成對的,可以歸為四組,所以,在程式設計時,只要從這4組燈中各

   取出一個燈,使這4個燈輪流變亮,與這4個燈方向對應的燈隨之一同變化,因此Lamp類中要有一個變數來記住自

   己相反方向的燈。在一個Lamp物件的變亮和變黑方法中,將對應方向的燈也變亮和變黑,每個燈變黑時,都伴隨

   著下一個燈的變亮,Lamp類中還需要一個變數來記住自己的下一個燈。

4) 無論在程式的什麼地方去獲得某個方向的燈時,每次獲得的都是同一個例項物件,所以Lamp類用列舉來做顯然有

   很大的方便性,永遠都只有代表12個方向的燈的例項物件。

四.程式碼實現

1.交通燈的Lamp類的程式碼

        1)系統中有12個方向上的燈,在程式的其他地方要根據燈的名稱就可以獲得對應的燈的例項物件,綜合這些因素,將Lamp類用java5中的列舉形式定義更為簡單。

        2)每個Lamp物件中的亮黑狀態用lighted變量表示,選用S2NS2WE2WE2N這四個方向上的Lamp物件依次輪詢變亮,Lamp物件中還要有一個oppositeLampName變數來表示它們相反方向的燈,再用一個nextLampName變數來表示此燈變亮後的下一個變亮的燈。這三個變數用構造方法的形式進行賦值,因為列舉元素必須在定義之後引用,所以無法再構造方法中彼此相互引用,所以,相反方向和下一個方向的燈用字串形式表示。

        3)增加讓Lamp變亮和變黑的方法:lightblackOut,對於S2NS2WE2WE2N這四個方向上的Lamp物件,這兩個方法內部要讓相反方向的燈隨之變亮和變黑,blackOut方法還要讓下一個燈變亮。

        4)除了S2NS2WE2WE2N這四個方向上的Lamp物件之外,其他方向上的Lamp物件的nextLampNameoppositeLampName屬性設定為null即可,並且S2NS2WE2WE2N這四個方向上的Lamp物件的nextLampNameoppositeLampName屬性必須設定為null,以便防止lightblackOut進入死迴圈。

<span style="font-size:18px;">//交通燈的Lamp類的程式碼
public enum Lamp {
	S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false),//列舉先定義再使用。不能在定義N2S之前呼叫
	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 boolean lighted;
	private	String opposite;
	private String next;
	
	private Lamp(){}
	
	private Lamp(String opposite,String next,boolean lighted){	//接收引數:對應的燈,下一個燈,當前燈的亮黑狀態						
		this.opposite = opposite;
		this.next = next;
		this.lighted = lighted;
	}
	
	
	public boolean isLighted() {				//獲取燈的亮黑狀態
		return lighted;
	}
	
	public void light() {
		this.lighted = true;
		if(opposite!=null){				//健壯性判斷,防止死迴圈,東對應的燈是西,西對應的燈沒有。
			Lamp.valueOf(opposite).light();		//根據物件名稱返回列舉。一個燈亮了,對應的燈也跟著亮。
		}
		System.out.println(name() + " lamp is green,下面總共應該有6個方向能看到汽車穿過!");   //四個右轉彎,;一對燈亮
	}
	
	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>

2.Road類的程式碼

1)每個Road物件都有一個name成員變數來代表方向,有一個vehicles(交通工具)成員變數來代表方向上的車輛。
2)在Road物件的構造方法中啟動一個執行緒每隔一個隨機的時間向vehicles集合中增加一輛車(用一個“路線名_id”

   形式的字串進行表示)。
3)在Road物件的構造方法中啟動一個定時器,每隔一秒檢查該方向上的燈是否為綠,是則列印車輛集合和將集合中的第一輛車移除掉。使用scheduleAtFixedRate方法。

<span style="font-size:18px;">import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//Road類
public class Road {
	List<String> vehicles = new ArrayList<String>();				//每條路線的車輛,裝入List集合
	private String name = null;
	
	public Road(String name) {										//每條路線都要命名
		this.name = name;
		
		//模擬汽車上路
		ExecutorService pool = Executors.newSingleThreadExecutor();				//建立單獨的執行緒,不停的往這個路線增加車輛
		pool.execute(new Runnable() {											//呼叫執行緒池中的執行器,執行空閒的執行緒
			public void run() {
				try {
					Thread.sleep(((long)(Math.random()*10)+1)*1000);			//1~10秒內隨機增加車輛
				} catch (InterruptedException e) {
					e.printStackTrace();
				}									
				for (int i = 0; i < 1000; i++) {
					vehicles.add(Road.this.name + "_" + i);						//外部類名.this.外部類成員變數名           訪問外部類成員變數,或者final修飾
				}
			}
		});
		
		//定時器,模擬汽車汽車穿過路口
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);	//建立一個定時器
		timer.scheduleAtFixedRate(												//固定頻率的定時器
				new Runnable(){
					public void run() {
						if(vehicles.size()>0){
							boolean lighted = Lamp.valueOf(Road.this.name).isLighted();//獲取燈的狀態
							if(lighted){
								System.out.println(vehicles.remove(0) + " is travling ");
							}
						}
					}}, 
				1,					//延遲1秒後開始執行任務
				1, 					//每隔一秒執行一次
				TimeUnit.SECONDS);
		
		
	}
}
</span>


3.LampController交通燈控制器

1)整個系統中只能有一套交通燈控制系統,所以,LampController類最好是設計成單例。
 
2)LampController構造方法中要設定第一個為綠的燈。
 
3)LampController物件的start方法中將當前燈變綠,然後啟動一個定時器,每隔10秒將當前燈變紅和將下一個燈變綠。

<span style="font-size:18px;">import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//交通燈控制器
public class LampController {
	private Lamp currentLamp;		//當前燈
	
	//初始化
	public LampController(){
		currentLamp = Lamp.S2N;		//預設第一個燈為S2N
		currentLamp.light();   	 	//點亮
		
		
		//每隔10秒鐘,變紅,下一個燈變綠.建立定時器執行緒池
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		
		timer.scheduleAtFixedRate(
				new Runnable() {
					public void run() {
						currentLamp = currentLamp.blackOut();//燈變黑同時指向下一個變綠的燈,blackOut()返回下一個變綠的燈。
					}
				}, 
				2,					//隔10秒執行第一次
				2, 					//每隔10秒執行一次
				TimeUnit.SECONDS);
	
	}
}
</span>


4.MainClass類

        1)用for迴圈創建出代表12條路線的物件。

        2)建立紅綠燈控制系統物件,啟動系統

/*
 * 12個燈:
 * S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S
 */
public class MainClass {

	public static void main(String[] args) {
		String[] derections = new String[]{
				"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"
		};
		for (int i = 0; i < derections.length; i++) {
			new Road(derections[i]);
		}
		
		new LampController();
	}

}

五.總結

用到的知識

1.List集合,用於儲存每條路線上的車輛。

2.ExecutorsExecutors.newSingleThreadExecutor(),建立新的執行緒,隨機在路線上增加車輛。

3.匿名內部類物件。在給執行緒的執行器傳遞任務時,建立了實現Runnable介面的子類的匿名內部類物件,匿名內部類

  要訪問外部類的成員變數時,可以寫成:外部類名.this.成員變數名。

4.定時器timer。

  ScheduledExecutorService timer =Executors.newScheduledThreadPool(1)

建立一個定時器。以及在LampController類中的控制紅綠燈時間的定時器。

5.列舉。在編寫Lamp類時使用了列舉,保證了無論什麼時候呼叫的燈都是同一個燈。

  但是要注意,列舉先定義,再使用

6.建立燈物件時,要是一個一個燈的new,new12個物件,很麻煩。可以使用循來建立例項物件,只要傳遞燈的名字即可。