銀行業務排程系統
一、概述
模拟實作銀行業務排程系統邏輯,具體需求如下:
銀行内有6個業務視窗,1- 4号視窗為普通視窗,5号視窗為快速視窗,6号視窗為VIP視窗。
有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費、電話費之類業務的客戶)。
異步随機生成各種類型的客戶,生成各類型使用者的機率比例為:
VIP客戶 :普通客戶 :快速客戶 = 1 :6 :3。
客戶辦理業務所需時間有最大值和最小值,在該範圍内随機設定每個VIP客戶以及普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間為最小值(提示:辦理業務的過程可通過線程Sleep的方式模拟)。
各類型客戶在其對應視窗按順序依次辦理業務。
當VIP(6号)視窗和快速業務(5号)視窗沒有客戶等待辦理業務的時候,這兩個視窗可以處理普通客戶的業務,而一旦有對應的客戶等待辦理業務的時候,則優先處理對應客戶的業務。
随機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設定。
二、需求分析
由題目可以了解到不管是客戶還是服務視窗,他們操作的都是同一個取票機,當客戶來到銀行辦理業務的時候,會到取票機器那裡取得一張号碼,同時該号碼會存儲在相對應的取票器中,這時就會有對應的視窗向對應的取票器中讀取存入的号碼,進而有序地對使用者進行操作。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TUPVTSU1UNJRkT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN3MzM0gDN5ADOxUDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
由需求可以知道,我們在一台取票機中得有三個取票器,分别管理着普通客戶、快速客戶和VIP客戶的取号,同時視窗也分為普通視窗、快速視窗和VIP視窗,規定普通視窗隻能辦理普通客戶的手續,VIP客戶可以辦理VIP客戶手續,如果在沒有VIP客戶的時候,也可以辦理普通客戶,快速視窗也一樣,在沒有快速客戶的時候,也可以辦理普通客戶的手續,如此分析,則有取票機的結構應為這樣:
一個取票機裡面有三個取票器,VIP視窗和快速視窗的操作功能是一樣,均為“做完自己的事,再忙其他的事”;基本流程圖如下:
普通視窗和VIP、快速視窗則不一樣,其基本流程圖如下:
到此,幾個取票器之間的基本關系就可以确立了下來了。在需求裡面有提到客戶服務時間與客服産生時間,這裡使用線程的sleep方法來模拟為客戶的服務的時間,使用jdk5的新特性來模拟客戶産生的過程,在這個系統中,個人覺得的就是類與類之間的關系太過于複雜了,是以按照了個人的了解畫出了類與類之間的關系圖。自定義的類有:
NunberManage:産生客服和取出客服的取票器
NumberMachine:模拟了一個取票機,裡面包含了三個取票器
ServiceWindow :模拟視窗去操作取票機,服務不同的客戶
MainClass:初始化各種視窗,以及設計了客戶已1:6:3來到銀行辦理業務
CustomerService:存儲在各個類中要使用到的常量
WindowType:枚舉類,存了三種視窗類型的對象。
很據我自己的了解得出的他們之間的關系;:
三、各個類的設計
NumberManage類的設計:
該類提供了兩個方法:
getNewNumber():當有客戶取服務号碼的時候,産生一個号碼,将此号碼傳回給客戶,使得客服指導知己的号碼,同時将這一号碼存儲在一個容器中,以便服務視窗能取到号碼,并為相應的客戶服務。
fetchNumber():為視窗調用,視窗取得集合中的号碼,并為該号碼對應的客戶服務。
由于這裡設計到了兩個函數同時操作一個對象(集合),是以這裡要使用同步鎖,使得在某一時刻隻能有一個方法區操作集合,避免了異常的産生。
該類的設計的源碼為:
package cn.itcast.bank;
import java.util.ArrayList;
import java.util.List;
//這裡産生為每一中類型的取票器提供兩種方法,
//提供為客戶取票和視窗取票兩種方法
public class NumberManage {
//使用一個集合,将客戶産生的号碼放入該集合中,友善視窗取得該号碼,為客戶服務
private List<Integer> NumberArr = new ArrayList<Integer>();
//在定義個變量,用來傳回客戶的号碼
private int lastNumber =0;
public synchronized Integer getNewNumber(){ //這裡是兩個函數同時操作同一個集合,為了避免産生異常,使用同步函數
NumberArr.add(++lastNumber); //客服取票,同時将客戶所取得的号碼存入集合中
return lastNumber;
}
public synchronized Integer fetchNumber(){
if(NumberArr.size() > 0){ //得閑判斷是否有需要服務的客戶
return NumberArr.remove(0);
}else{
return null;
}
}
}
NumberMachine類的設計:
該類模拟了一個取票機,保證該取票機隻能有一個,同時也要得到該取票機中的三種取票器,
是以該類的設計就是建立NumberManage類的對象,對外提供擷取該類對象的方法,同時,使用單例設計模式讓本類永遠都隻能有一個執行個體對象:
該類源碼為:
package cn.itcast.bank;
/*
* 該類模拟了一個取票機,該取票機裡有三個取票器,分别是普通客戶取票器、VIP客戶取票器和快速客戶取票器
*
* 三個取票器有各自的NumberManage
*
* 為了産生避免産生多個相同的号碼,是以這裡采用了單例設計模式,也就是這裡隻有一個取票機
*
*
* */
public class NumberMachine {
//建立三個取票器
//普通客服取票器
private NumberManage commonCustomer = new NumberManage();
//VIP客戶取票器
private NumberManage VIPCustomer = new NumberManage();
//快速客戶取票器
private NumberManage fastCustomer = new NumberManage();
public NumberManage getCommonCustomer() {
return commonCustomer;
}
public NumberManage getVIPCustomer() {
return VIPCustomer;
}
public NumberManage getFastCustomer() {
return fastCustomer;
}
//控制本類永遠隻有一個對象,單例設計模式
private NumberMachine(){};
private static NumberMachine instance = new NumberMachine();
public static NumberMachine getInstance(){
return instance;
}
}
ServiceWindow類的設計:
該類可以為各種類型的客戶提供服務,也就是說這個類可以模拟不同的視窗,并為不同的客戶提供服務,這裡具體有什麼樣的視窗,還要由MainClass類中指定,這裡指定了什麼樣的視窗應該為什麼樣的客戶服務,使用jdk1.5的新特性,開啟一個線程來執行取票任務,使用循環,不停地取票,并判斷目前視窗是那種類型的視窗,使用目前視窗去執行對應的取票任務(VIP視窗在VIP取票器中取票,快速視窗在快速取票器中取票。。。。),對于VIP取票和快速取票,如果目前沒有VIP客戶,則VIP視窗将會為普通客戶服務,快速視窗也一樣,
setType:用來設定目前視窗是什麼類型的,
setWinNumber:用來設定目前視窗是第幾個視窗。
start();視窗開啟,開始服務
CommCustomer();為普通客戶提供服務,沒有普通客戶時,休息1s,有,為該客戶服務,并在一定時間内不為其他客戶服務(線程睡眠一端時間);
FastCustomer();為快速客戶提供服務,有快速客戶時,為快速客服提供服務,在服務期間也不為其他客戶提供服務(線程睡眠一段時間),如果沒有快速客戶,則該快速視窗就會為普通客戶提供服務,此時有普通客戶,為普通客戶提供服務,沒有,休息1s,再繼續去取快速客戶,如此循環。
VipCustomer();該方法的功能和FastCustomer的功能相似;
該類的源碼實作:
package cn.itcast.bank;
import java.util.Random;
import java.util.concurrent.Executors;
//模拟服務視窗
public class ServiceWindow {
//初始化視窗
WindowType type = WindowType.COMMON;
//初始化視窗
private int winNumber;
//開始取票
public void setType(WindowType type) {
this.type = type;
}
public void setWinNumber(int winNumber) {
this.winNumber = winNumber;
}
public void start(){
//開啟一個線程來執行視窗取票
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
switch (type) {
case COMMON: //如果是普通視窗
ConmmNumber();
break;
case FAST:
FastNumber();
break;
case VIP:
VipNumber();
break;
}
}
}
});
}
protected void ConmmNumber() {
// TODO Auto-generated method stub
//獲得取票機中取票器
NumberManage common = NumberMachine.getInstance().getCommonCustomer();
//擷取該取票器中的号碼
Integer CustomerNumber = common.fetchNumber();
if(CustomerNumber != null){
//取到号碼,為該客戶服務
System.out.println("第"+winNumber+"号"+type+"視窗正在為"+CustomerNumber+"号普通使用者服務");
//服務期間,不能服務其他客戶,讓該線程睡眠一段時間,
int Time = CustomerService.WINDOW_SERVICE_MAX_TIME-CustomerService.WINDOW_SERVICE_MIN_TIME;
int currentTime = new Random().nextInt(Time)+1 + CustomerService.WINDOW_SERVICE_MIN_TIME;
try {
Thread.sleep(currentTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("完成對"+CustomerNumber+"号普通客戶的服務,總耗時"+currentTime/1000+"s");
}else{
System.out.println("第"+winNumber+"号"+type+"視窗沒有取到普通客戶,休息1s");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected void FastNumber() {
// TODO Auto-generated method stub
//獲得取票機中取票器
NumberManage common = NumberMachine.getInstance().getFastCustomer();
//擷取該取票器中的号碼
Integer CustomerNumber = common.fetchNumber();
if(CustomerNumber != null){
//取到号碼,為該客戶服務
System.out.println("第"+winNumber+"号"+type+"視窗正在為"+CustomerNumber+"号快速客戶服務");
//服務期間,不能服務其他客戶,讓該線程睡眠一段時間,
//int Time = CustomerService.WINDOW_SERVICE_MAX_TIME-CustomerService.WINDOW_SERVICE_MIN_TIME;
// int currentTime = new Random().nextInt(CustomerService.WINDOW_SERVICE_MIN_TIME)+1;
try {
Thread.sleep(CustomerService.WINDOW_SERVICE_MIN_TIME);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("完成對"+CustomerNumber+"号快速客戶的服務,總耗時"+CustomerService.WINDOW_SERVICE_MIN_TIME/1000+"s");
}else{
System.out.println("第"+winNumber+"号"+type+"視窗沒有取到"+type+"客戶,可以為普通客戶服務");
ConmmNumber();
}
}
protected void VipNumber() {
// TODO Auto-generated method stub
//獲得取票機中取票器
NumberManage common = NumberMachine.getInstance().getVIPCustomer();
//擷取該取票器中的号碼
Integer CustomerNumber = common.fetchNumber();
if(CustomerNumber != null){
//取到号碼,為該客戶服務
System.out.println("第"+winNumber+"号"+type+"視窗正在為"+CustomerNumber+"号VIP客戶服務");
//服務期間,不能服務其他客戶,讓該線程睡眠一段時間,
int Time = CustomerService.WINDOW_SERVICE_MAX_TIME-CustomerService.WINDOW_SERVICE_MIN_TIME;
int currentTime = new Random().nextInt(Time)+1 + CustomerService.WINDOW_SERVICE_MIN_TIME;
try {
Thread.sleep(currentTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("完成對"+CustomerNumber+"号VIP客戶的服務,總耗時"+currentTime/1000+"s");
}else{
System.out.println("第"+winNumber+"号"+type+"視窗沒有取到"+type+"客戶,可以為普通客戶服務");
ConmmNumber();
}
}
}
MainClass類的設計:
該類就做了一些初始化啊,這裡就初始化了五個普通視窗,一個快速視窗,一個VIP視窗,并産生三個新線程去模拟三種不同的客戶的産生的情況,并實作
VIP客戶 :普通客戶 :快速客戶 = 1 :6 :3。
該類源碼:
package cn.itcast.bank;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainClass {
public static void main(String[] args) {
//開啟五個普通視窗,不指定則預設是普通視窗
for(int i =1 ;i <= 5 ; i++){
ServiceWindow Commwindow = new ServiceWindow();
Commwindow.setWinNumber(i);
Commwindow.start();
}
//開啟一個VIP視窗
ServiceWindow Vipwindow = new ServiceWindow();
Vipwindow.setType(WindowType.VIP);
Vipwindow.setWinNumber(1);
Vipwindow.start();
//開啟一個快速視窗
ServiceWindow Fastwindow = new ServiceWindow();
Fastwindow.setType(WindowType.FAST);
Fastwindow.setWinNumber(1);
Fastwindow.start();
//開啟視窗,先在就是上班時間,該有客戶來辦理業務了,由于三種客戶之間是沒有任何聯系的,是以每一種客戶要使用單獨的線程來控制
//普通客戶
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Integer Customer = NumberMachine.getInstance().getCommonCustomer().getNewNumber();
System.out.println("第"+Customer+"号普通客戶正在等待辦理業務");
}
},
0,
CustomerService.HOW_TIME_ADDCUSTOMER,
TimeUnit.SECONDS);
//VIP客戶
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Integer Customer = NumberMachine.getInstance().getVIPCustomer().getNewNumber();
System.out.println("第"+Customer+"号VIP客戶正在等待辦理業務");
}
},
0,
CustomerService.HOW_TIME_ADDCUSTOMER * 6,
TimeUnit.SECONDS);
//快速客戶
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Integer Customer = NumberMachine.getInstance().getFastCustomer().getNewNumber();
System.out.println("第"+Customer+"号快速客戶正在等待辦理業務");
}
},
0,
CustomerService.HOW_TIME_ADDCUSTOMER * 2,
TimeUnit.SECONDS);
}
}
對于CustomerService類存儲的就是一些在其他類中的常量:
package cn.itcast.bank;
public class CustomerService {
//一個視窗為客戶服務的最大時間為
public static final int WINDOW_SERVICE_MAX_TIME = 10000;
//一個視窗為客戶服務的最大時間為
public static final int WINDOW_SERVICE_MIN_TIME = 1000;
//設定多長時間增加一個客戶
public static final int HOW_TIME_ADDCUSTOMER = 1;
}
WindowType是一個枚舉類,該枚舉類有三個執行個體對象,分别是:COMMON,VIP,FAST,他們分别代表了視窗的類型為:普通類型,VIP類型和快速視窗類型,這裡覆寫了他們的toString方法,
package cn.itcast.bank;
public enum WindowType {
COMMON,VIP,FAST;
@Override
public String toString(){
switch (this) {
case COMMON:
return "普通";
case VIP:
return name();
case FAST:
return "快速";
default:
break;
}
return null;
}
}