------- android培訓、java培訓、期待與您交流! ----------
銀行業務調度系統
模擬實現銀行業務調度系統邏輯,具體需求如下:
1、銀行內有6個業務窗口,1- 4號窗口爲普通窗口,5號窗口爲快速窗口,6號窗口爲VIP窗口。
2、有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費、電話費之類業務的客戶)。
3、異步隨機生成各種類型的客戶,生成各類型用戶的概率比例爲:
4、VIP客戶 :普通客戶 :快速客戶 = 1 :6 :3。
5、客戶辦理業務所需時間有最大值和最小值,在該範圍內隨機設定每個VIP客戶以及普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間爲最小值(提示:辦理業務的過程可通過線程Sleep的方式模擬)。
6、各類型客戶在其對應窗口按順序依次辦理業務。
7、當VIP(6號)窗口和快速業務(5號)窗口沒有客戶等待辦理業務的時候,這兩個窗口可以處理普通客戶的業務,而一旦有對應的客戶等待辦理業務的時候,則優先處理對應客戶的業務。
8、隨機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設置。
9、不要求實現GUI,只考慮系統邏輯實現,可通過Log方式展現程序運行結果。
根據去本地的農業銀行去觀察後,發現:
①、當我進入辦理大廳的時候,不是直接去找櫃檯的人員,而是從一臺機器上面選擇,我來幹什麼事。
②、從機器上面就會出現一張小票,上面有一個號碼
③、當櫃檯每辦理完一個人的業務以後,就會叫一個號碼,這個時候對應的號碼則過去辦理業務。
其實:當我去機器上面點擊產生號碼以後,他們的內部就會有一個排隊的信息。(當櫃檯的某一個工作位置空閒時,則按照排隊的順序進行取出)
下面借住張老師的一張程序圖(自己小小修改了一下):
代碼演示:設計一個號碼管理,這個號碼管理器管理則 客戶來生成 一個 獨立的id編號,而且還提供了給銀行櫃檯人員按照順序獲取編號,每獲取一個則少一個。
①、NumberManager類:
import java.util.ArrayList;
import java.util.List;
public class NumberManager //號碼管理器。號碼管理器是 內嵌在 號碼生成器中
{
private Integer numberId = 1;
List<Integer> idList = new ArrayList<Integer>();
public synchronized Integer numberGenerator() //號碼生成器,給人們使用,產生一個號碼(隊列結構)
{
idList.add(numberId);
return numberId++;
}
public synchronized Integer numberFetch() //給窗口的工作人員使用的
{
if(idList.size()>0)
{
return idList.remove(0);
}
else
return null;
}
}
②、代碼演示:當有一個號碼管理器以後,由於有3個不同的服務櫃檯,所服務的類別也有所差別。但是設計的時候他們最好都是從1臺機器上面出來的。因爲不可能爲某一個服務櫃檯去專門設計(一臺號碼生成器和號碼管理機器)。 我們設計出來的機器包含了3種服務類別,並且只能有一臺機器。(只能有一臺的話,就保證了唯一性,所以使用單例設計模式)
NumberMachine類:
public class NumberMachine //3個不同的號碼管理器生成一臺機器,該機器不能在創建對象。必須單例
{
private NumberManager commonManager = new NumberManager(); //創建普通櫃檯的 號碼管理器
private NumberManager fastManager = new NumberManager(); //創建快速櫃檯的 號碼管理器
private NumberManager vipManager = new NumberManager(); //創建VIP櫃檯的 號碼管理器
public NumberManager getCommonManager() //返回普通櫃檯 的管理器對象,用作生成號碼,和取號
{
return commonManager;
}
public NumberManager getFastManager()//返回快速櫃檯 的管理器對象,用作生成號碼,和取號
{
return fastManager;
}
public NumberManager getVipManager()//返回vip櫃檯 的管理器對象,用作生成號碼,和取號
{
return vipManager;
}
/**創建單例的方法(餓漢式):
* 1、私有化構造方法
* 2、在內部自己創建一個自己的對象,並且私有化
* 3、通過一個靜態方法,返回自己的對象。
* */
private NumberMachine(){}
private static NumberMachine instance = new NumberMachine();
public static NumberMachine getInstance()
{
return instance;
}
}
代碼演示:有了機器 現在設計 窗口,窗口的類型 只能有“普通,快速,vip”,所以呢。我們複習一下 enum 枚舉。
設計一個窗口,在構造方法中傳入 他的 類型(枚舉類型),和 窗口 編號(int)
設計一個方法,該方法可以開啓一個獨立的線程,該方法不斷的 去 取自己對應的號碼,如果取到則 進行下一步處理,如果沒有取到 則在屏幕打印休息一會兒 sleep。
如果 他 的類型是 vip 和 快速的話,他們還要查看一下 普通櫃檯是否需要幫忙。如果有就進行處理,如果還是沒有,就在屏幕打印休息一會兒 sleep。
③Type 枚舉類:
public enum Type
{
COMMON,FAST,VIP;
public String toString()
{
switch(this) //調用我名字的時候,我就返回字符串
{
case COMMON:
return "普通";
case FAST:
return "快速";
case VIP:
return "VIP";
}
return null;
}
}
開發過程中,將所有常使用的數字都封裝到一起(都是靜態的,都是不可變的)提高閱讀性:
④、StaticFinalFiedl():
public class StaticFinalField
{
public static final int SLEEP_MAX = 10000; //辦理業務最大時間
public static final int SLEEP_MIN = 1000; //辦理業務最小時間
}
⑤、ServiceWindow 服務窗口類:下面代碼有點亂,還請老師多多包含
import java.util.Random;
import java.util.concurrent.Executors;
public class ServiceWindow //在構造方法中,需要傳入 窗口類別,和窗口號
{
private Type type = null;
private int windowId ;
public ServiceWindow(Type type,int windowId)
{
this.type = type;
this.windowId = windowId;
}
public void start() //調用我就開啓一個線程
{
Executors.newSingleThreadExecutor().execute(new Runnable(){
public void run()
{
while(true)
{
switch(type)
{
case COMMON:
commonService();
break;
case FAST:
fastService();
break;
case VIP:
vipService();
break;
}
}
}
private void commonService() //如果屬於 COMMON 類別。則使用這裏面的方法
{
System.out.println("第" + windowId + "號" + type +"窗口:取普通客戶--------");
Integer commonNumber = NumberMachine.getInstance().getCommonManager().numberFetch();
if(commonNumber !=null)
{
System.out.println("第" + windowId + "號" + type + "窗口:正在爲“" + commonNumber + "號”普通客人服務");
/**生成普通客戶 辦理業務的 隨機時間
* */
int sleepTime = new Random().nextInt(StaticFinalField.SLEEP_MAX - StaticFinalField.SLEEP_MIN) + StaticFinalField.SLEEP_MIN;
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("第" + windowId + "號" + type + "窗口:爲" + commonNumber + "號普通客戶服務\"" + sleepTime/1000 + "\"秒" );
}
else
{
System.out.println("第" + windowId + "號" + type + "窗口:沒有獲取到普通客戶,休息一秒" );
try
{
Thread.sleep(StaticFinalField.SLEEP_MIN);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private void fastService()
{
System.out.println("第" + windowId + "號" + type +"窗口:取"+ type +"客戶--------");
Integer fastNumber = NumberMachine.getInstance().getFastManager().numberFetch();
if(fastNumber !=null)
{
System.out.println("第" + windowId + "號" + type + "窗口:正在爲“" + fastNumber + "號" + type +"”客人服務");
/**快速客戶繳費就走。所以使用服務的時間是最短的
* */
try
{
Thread.sleep(StaticFinalField.SLEEP_MIN);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("第" + windowId + "號" + type + "窗口:爲“" + type + fastNumber + "”號客戶服務\"" + StaticFinalField.SLEEP_MIN/1000 + "\"秒" );
}
else
{
System.out.println("第" + windowId + "號" + type + "窗口:" + "沒有獲取到快速客戶");
/**如果快速客戶沒有用戶的話,那麼快速客戶將去查看普通客戶是否需要幫忙的
* */
commonService();
}
}
private void vipService()
{
System.out.println("第" + windowId + "號" + type +"窗口:取" + type + "客戶--------");
Integer vipNumber = NumberMachine.getInstance().getVipManager().numberFetch();
if(vipNumber !=null)
{
System.out.println("第" + windowId + "號" + type + "窗口:正在爲“" + vipNumber + "號" + type + "”客人服務");
/**生成vip客戶 辦理業務的 隨機時間
* */
int sleepTime = new Random().nextInt(StaticFinalField.SLEEP_MAX - StaticFinalField.SLEEP_MIN) + StaticFinalField.SLEEP_MIN;
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("第" + windowId + "號" + type + "窗口:爲" + vipNumber + "號" + type + "客戶服務\"" + sleepTime/1000 + "\"秒" );
}
else
{
System.out.println("第" + windowId + "號" + type + "窗口:" + "沒有獲取到vip客戶");
/**沒有vip客戶。則去幫忙普通客戶做業務
* */
commonService();
}
}
});
}
}
⑥、PersonRandom():
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PersonRandom
{
PersonRandom()
{
Executors.newScheduledThreadPool(1).scheduleAtFixedRate( //第一次1秒,隨後每秒運行一次線程,模擬來的普通客戶
new Runnable(){
public void run()
{
System.out.println("來了一個普通客戶,等待服務");
NumberMachine.getInstance().getCommonManager().numberGenerator();
System.out.println("普通客戶距離排隊還有:" + NumberMachine.getInstance().getCommonManager().idList.size());
}
},
1,
1,
TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate( //第一次1秒,隨後2秒運行一次線程,模擬來的快速客戶
new Runnable(){
public void run()
{
System.out.println("來了一個快速客戶,等待服務");
NumberMachine.getInstance().getFastManager().numberGenerator();
System.out.println("快速客戶距離排隊還有:" + NumberMachine.getInstance().getFastManager().idList.size());
}
},
1,
2,
TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate( //第一次3秒,隨後6秒運行一次線程,模擬來的VIP客戶
new Runnable(){
public void run()
{
System.out.println("來了一個vip客戶,等待服務");
NumberMachine.getInstance().getVipManager().numberGenerator();
System.out.println("vip客戶距離排隊還有:" + NumberMachine.getInstance().getVipManager().idList.size());
}
},
3,
6,
TimeUnit.SECONDS);
}
}
⑦、所有的類都設計完成以後:他們都 只是一個 普通的類,沒有辦法將他們運行起來。在創建一個 MainClass 用作運行他們。
public class MainClass {
public static void main(String[] args)
{
new PersonRandom(); //構造方法中,開了3線程。模擬 3種客戶來的頻率
for(int i=1; i<5; i++) //開啓了4個普通窗口線程
{
new ServiceWindow(Type.COMMON,i).start();
}
new ServiceWindow(Type.FAST,5).start(); //開啓快速窗口線程
new ServiceWindow(Type.VIP,6).start(); //開啓vip窗口線程
}
}
還是附上我的效果,我想應該是正確的
第1號普通窗口:取普通客戶--------
第3號普通窗口:取普通客戶--------
第2號普通窗口:取普通客戶--------
第5號快速窗口:取快速客戶--------
第4號普通窗口:取普通客戶--------
第6號VIP窗口:取VIP客戶--------
第1號普通窗口:沒有獲取到普通客戶,休息一秒
第3號普通窗口:沒有獲取到普通客戶,休息一秒
第2號普通窗口:沒有獲取到普通客戶,休息一秒
第5號快速窗口:沒有獲取到快速客戶
第5號快速窗口:取普通客戶--------
第5號快速窗口:沒有獲取到普通客戶,休息一秒
第4號普通窗口:沒有獲取到普通客戶,休息一秒
第6號VIP窗口:沒有獲取到vip客戶
第6號VIP窗口:取普通客戶--------
第6號VIP窗口:沒有獲取到普通客戶,休息一秒
來了一個普通客戶,等待服務
普通客戶距離排隊還有:1
來了一個快速客戶,等待服務
快速客戶距離排隊還有:1
第3號普通窗口:取普通客戶--------
第1號普通窗口:取普通客戶--------
第3號普通窗口:正在爲“1號”普通客人服務
第5號快速窗口:取快速客戶--------
第6號VIP窗口:取VIP客戶--------
第6號VIP窗口:沒有獲取到vip客戶
第6號VIP窗口:取普通客戶--------
第2號普通窗口:取普通客戶--------
第4號普通窗口:取普通客戶--------
第2號普通窗口:沒有獲取到普通客戶,休息一秒
第6號VIP窗口:沒有獲取到普通客戶,休息一秒
第5號快速窗口:正在爲“1號快速”客人服務
第1號普通窗口:沒有獲取到普通客戶,休息一秒
第4號普通窗口:沒有獲取到普通客戶,休息一秒
來了一個普通客戶,等待服務
普通客戶距離排隊還有:1
第6號VIP窗口:取VIP客戶--------
第5號快速窗口:爲“快速1”號客戶服務"1"秒
第5號快速窗口:取快速客戶--------
第5號快速窗口:沒有獲取到快速客戶
第5號快速窗口:取普通客戶--------
第5號快速窗口:正在爲“2號”普通客人服務
第2號普通窗口:取普通客戶--------
第6號VIP窗口:沒有獲取到vip客戶
第6號VIP窗口:取普通客戶--------
第6號VIP窗口:沒有獲取到普通客戶,休息一秒
第2號普通窗口:沒有獲取到普通客戶,休息一秒
第4號普通窗口:取普通客戶--------
第4號普通窗口:沒有獲取到普通客戶,休息一秒
第1號普通窗口:取普通客戶--------
第1號普通窗口:沒有獲取到普通客戶,休息一秒
尊敬的老師:ServiceWindow 的代碼稍微有點囉嗦,有點亂,還請老師多多包含一下。
銀行調度系統學習的知識:小結
NumberManager類:①、隊列數據結構(先進先出。我想如果使用LinkedList 可能效率要更高點兒 )。 ②、後++,先進行賦值,然後在自增。
NumberMachine類:①、單例設計模式。②我自己創建了其他類對象,通過我的方法給你返回其他類的對象,保證其他類的唯一性。
Type 枚舉類:①、你只能使用我這裏面的元素(限定元素)。②在 覆蓋toString的時候, switch 後面的括號跟上this,本來對象。
StaticFinalField類:①既然是固定的元素值的話,就 static ,然後也不能改變。(我想話,應該還是可以使用 枚舉來完成,不過枚舉的 參數列表必須是同一個類型)
ServiceWindow類:①創建一個普通類,通過 一個方法 裏面 嵌套 線程池 就可以開啓一個線程(當每一個對象不同的話,就可以開啓不同的線程,我現在感覺Thread就有那麼一點兒像)②複習了一下 switch 。 ③(其實應該像張老師說的那樣,使用模版設計模式,老師海涵,我想進入44期)。
PersonRandom類:①這是在紅綠燈學到的,創建一個類,然後在這個類中 嵌入 線程,只要 其他 地方 實例化 本類,那麼 我 嵌入的線程就開啓了。