天天看點

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

            銀行業務排程系統

一、概述

模拟實作銀行業務排程系統邏輯,具體需求如下:

     銀行内有6個業務視窗,1- 4号視窗為普通視窗,5号視窗為快速視窗,6号視窗為VIP視窗。

   有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費、電話費之類業務的客戶)。

 異步随機生成各種類型的客戶,生成各類型使用者的機率比例為:

        VIP客戶 :普通客戶 :快速客戶 =  1 :6 :3。

     客戶辦理業務所需時間有最大值和最小值,在該範圍内随機設定每個VIP客戶以及普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間為最小值(提示:辦理業務的過程可通過線程Sleep的方式模拟)。

      各類型客戶在其對應視窗按順序依次辦理業務。

      當VIP(6号)視窗和快速業務(5号)視窗沒有客戶等待辦理業務的時候,這兩個視窗可以處理普通客戶的業務,而一旦有對應的客戶等待辦理業務的時候,則優先處理對應客戶的業務。

   随機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設定。

二、需求分析

由題目可以了解到不管是客戶還是服務視窗,他們操作的都是同一個取票機,當客戶來到銀行辦理業務的時候,會到取票機器那裡取得一張号碼,同時該号碼會存儲在相對應的取票器中,這時就會有對應的視窗向對應的取票器中讀取存入的号碼,進而有序地對使用者進行操作。

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

由需求可以知道,我們在一台取票機中得有三個取票器,分别管理着普通客戶、快速客戶和VIP客戶的取号,同時視窗也分為普通視窗、快速視窗和VIP視窗,規定普通視窗隻能辦理普通客戶的手續,VIP客戶可以辦理VIP客戶手續,如果在沒有VIP客戶的時候,也可以辦理普通客戶,快速視窗也一樣,在沒有快速客戶的時候,也可以辦理普通客戶的手續,如此分析,則有取票機的結構應為這樣:

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

一個取票機裡面有三個取票器,VIP視窗和快速視窗的操作功能是一樣,均為“做完自己的事,再忙其他的事”;基本流程圖如下:

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

普通視窗和VIP、快速視窗則不一樣,其基本流程圖如下:

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

到此,幾個取票器之間的基本關系就可以确立了下來了。在需求裡面有提到客戶服務時間與客服産生時間,這裡使用線程的sleep方法來模拟為客戶的服務的時間,使用jdk5的新特性來模拟客戶産生的過程,在這個系統中,個人覺得的就是類與類之間的關系太過于複雜了,是以按照了個人的了解畫出了類與類之間的關系圖。自定義的類有:

NunberManage:産生客服和取出客服的取票器

NumberMachine:模拟了一個取票機,裡面包含了三個取票器

ServiceWindow :模拟視窗去操作取票機,服務不同的客戶

MainClass:初始化各種視窗,以及設計了客戶已1:6:3來到銀行辦理業務

CustomerService:存儲在各個類中要使用到的常量

WindowType:枚舉類,存了三種視窗類型的對象。

很據我自己的了解得出的他們之間的關系;:

黑馬程式員_7K面試題之銀行業務排程系統            銀行業務排程系統

三、各個類的設計

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;
		
	}
	
	

}