银行业务调度系统
一、概述
模拟实现银行业务调度系统逻辑,具体需求如下:
银行内有6个业务窗口,1- 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
二、需求分析
由题目可以了解到不管是客户还是服务窗口,他们操作的都是同一个取票机,当客户来到银行办理业务的时候,会到取票机器那里取得一张号码,同时该号码会存储在相对应的取票器中,这时就会有对应的窗口向对应的取票器中读取存入的号码,从而有序地对用户进行操作。
一个取票机里面有三个取票器,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;
}
}