您的位置:首页 > 职场人生

java 面试题(二) 银行业务调度系统

2013-01-07 20:25 609 查看

银行业务调度系统

一 , 需求描述:

模拟实现银行业务调度系统逻辑,具体需求:

1,银行内有六个业务窗口,1-4号为普通窗口,5号为快速窗口,6号为VIP窗口

2,有三种类型的客户:VIP客户,普通客户,快速客户

3,异步随机产生各种类型的客户,生成各客户的概率比例为:

VIP客户 : 快速客户 : 普通客户 = 1:3:6

4,客户业务所需时间有最大最小值,快速客户办理业务所需时间为最小值

VIP客户和普通客户所需时间为最大最小值之间范围的随机时间

(办理业务的过程可通过线程Sleep的方式模拟)。

5,各类型客户在其对应窗口按顺序依次办理业务。

6,当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,

这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务

的时候,则优先处理对应客户的业务。

7,随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。

8,不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

二,面向对象分析和设计

(1)面向对象分析

从需求看.问题涉及到:

服务窗口,客户,排队号码机

排队号码机能够提供为三种客户分别产生排队号的功能,并且能够在服务窗口服务

完毕一个客户时,删除对应排队号,三种号码机各自管理一类用户的排队号码 ,由于

客户是按概率比例随机产生的 , 对应于三个号码机随机产生号码,为了协调三个号

码机随机产生的频率以及比例则由号码机的调度器管理,,需要一个号码机调度器,

整个系统中只需要一个该调度器故将其设计成单例.

号码机调度器包装着三个号码机的对象,通过号码调度器可以直接与服务窗口通讯

客户被分为三种类型,对应可以从三个号码机获取一个排队号,故可以将客户类型

定义为枚举,三种客户对应三个枚举元素

服务窗口有三种 , 对应服务三种客户,另外5号快速窗口和6号VIP窗口还能够优先

对应客户,在没有对应客户时服务普通客户功能

服务窗口具有叫号功能,叫号时

(1),通过号码机的调度器获知当前对应号码机有无处于等待状态的号码,有则从中取

号叫号,如果没有

(2),对于普通窗口会先休息一下1秒后返回继续执行上面(1)步

(3),对于快速窗口或VIP窗口会从普通号码机上获知有无等待状态的号码,有则获取叫

号,没有就休息1秒后返回执行(1)步

叫到号后,通过定义一个一定范围内的随机的时间间隔线程等待功能来模拟为一个客户

服务所用的时间



(2)面向对象设计

1) 队号机对应抽象为NumberMachine类

需要定义一个成员变量来表示排队号,每次产生对应一个客户排队号码变量自增一次

需要定义一个用于存储所有等待服务的客户号码的队列集合,集合的添加删除元素就是

产生客户号码和删除已经服务完毕的客户号码的过程。

需要定义两个功能一个是产生新号码的方法内部给集合添加元素,元素就是排队号

一个获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,

所以,要进行同步。

代码:

package com.itheima.bankqueque;

import java.util.ArrayList;
import java.util.List;
/**
*
* 排队号机
* @author Administrator
* */
public class NumberMachine {

private int lastNumber = 0;//排队编号
private List<Integer> queueNumbers = new ArrayList<Integer>();//用集合来存放等待到服务结束的号码

/*产生排队号码,对应客户通过对应排队号码机的该方法来取得排队号*/
public synchronized Integer generateNewNumber(){
queueNumbers.add(++lastNumber);
return lastNumber;
}

/*服务窗口服务完毕对应排队号的客户时调用该方法,表示持该号码的客户已服务结束*/
public synchronized Integer fetchNumber(){

if(queueNumbers.size()>0){
return (Integer)queueNumbers.remove(0);
}else{
return null;
}
}
}


2) 号码机的调度器抽象为 NumberMachineManager类

定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户

的号码机,定义三个对应的方法来返回这三个NumberManager对象。

将NumberMachine类设计成单例。

代码:

package com.itheima.bankqueque;

/**
*
* 协调管理三种客户排队号码机的管理器
**/

public class NumberMachineManager {
/*将排队号码产生器设计为单例*/
private static NumberMachineManager instance = new NumberMachineManager();
/*产生三个排队号码管理器,分别管理三种客户的排队号*/
private NumberMachine commonManager = new NumberMachine();
private NumberMachine expressManager = new NumberMachine();
private NumberMachine vipManager = new NumberMachine();

private NumberMachineManager(){}
/*获取单例对象*/
public static NumberMachineManager getInstance(){
return instance;
}
/*获取普通客户排队号管理器*/
public NumberMachine getCommonManager() {
return commonManager;
}
/*获取快速客户排队号管理器*/
public NumberMachine getExpressManager() {
return expressManager;
}
/*获取VIP客户排队号管理器*/
public NumberMachine getVipManager() {
return vipManager;
}

}


3) 客户类型抽象为 CustomerType枚举类

系统中有三种类型的客户,所以用定义为枚举类,每个元素对应表示一种客户类型。

重写toString方法,返回对应客户类型的中文名称。

代码:

package com.itheima.bankqueque;

/*定义客户类型*/
public enum CustomerType {
COMMON,EXPRESS,VIP;

/**
* 返回当前客户类型对应的名称
* */

@Override
public String toString(){
String name = null;
switch(this){
case COMMON:
name = "普通";
break;
case EXPRESS:
name = "快速";
break;
case VIP:
name = name();
break;
}
return name;
}
}


4) 服务窗口抽象为 ServiceWindow类

定义三个方法分别对应于为三种客户进行服务.

定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个对应服务方法。

定义三个服务三种客户类型的方法,具体实现服务窗口服务三种客户的功能,包括叫号功能,所有类型

的窗口在都先通过调度器获取到对应的号码机,并通过号码机获取对应的客户

号码,如果对应号码机中存在号码则获取叫号,如果没有,那么对应普通窗口会等待1秒再继续尝试获取

对于VIP和快速窗口从对应号码机中获取不到号码时会继续从普通号码机中去尝试获取,如果获取到

则为普通客户服务,获取不到则休息1秒 后继续尝试从对应的号码机中获取客户号码,依次循环

通过线程的随机时间间隔等待来模拟为一个客户服务

代码:

package com.itheima.bankqueque;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

/**
*
*服务窗口
**/
public class ServiceWindow {
private static Logger logger = Logger.getLogger("cn.itcast.bankqueue");

private CustomerType type = CustomerType.COMMON;//初始化为普通客户
private int number = 1;//窗口编号,初始化为1号窗口

//返回当前服务客户类型
public CustomerType getType(){
return type;
}

//设置当前服务客户类型
public void setType(CustomerType type) {
this.type = type;
}

//设置当前服务窗口号
public void setNumber(int number){
this.number = number;
}

/**
*
* 启动窗口服务
* */
public void start(){
/*
* 采用JDK1.5的新的创建线程方法创建并启动线程
* 通过匿名内部类直接实现Runnable
* */
Executors.newSingleThreadExecutor().execute(
new Runnable(){
public void run(){
/*通过循环不断检查当前的type并执行各个服务窗口的对应服务*/
while(true){
switch(type){
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}
}
}
);
}

/*普通窗口的服务方法*/
private void commonService(){
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取普通任务!");

//普通窗口在调用调度器的获取普通号码机,并通过普通号码机尝试获取存在的客户号码

Integer serviceNumber = NumberMachineManager.getInstance().getCommonManager().fetchNumber();

//假如获取到号码,则为之服务
if(serviceNumber != null ){
System.out.println(windowName + "开始为第" + serviceNumber + "号普通客户服务");
//最大时间时间
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
//获取服务时间,1~10秒
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
//模拟正在为客户服务
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号普通客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//如果没有获取到号码则休息1秒
System.out.println(windowName + "没有取到普通任务,正在空闲一秒");

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void expressService(){
//快速窗口通过调度器获取其中的快速客户号码机的号码
Integer serviceNumber = NumberMachineManager.getInstance().getExpressManager().fetchNumber();

String windowName = "第" + number + "号" + type + "窗口";

System.out.println(windowName + "开始获取快速任务!");
//假如获取到号码,则为之服务
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号快速客户服务");
//为快速客户服务的时间为1秒
int serviceTime = Constants.MIN_SERVICE_TIME;
//模拟正在为快速客户服务
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号快速客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//如果快速窗口没有获取到快速客户号码则启动普通服务方法,执行普通窗口一样的功能
System.out.println(windowName + "没有取到快速任务!");
commonService();
}
}

/**
* VIP服务窗口
*/
private void vipService(){

//通过号码机调度器获取VIP号码机的客户号码
Integer serviceNumber = NumberMachineManager.getInstance().getVipManager().fetchNumber();
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取VIP任务!");
//假如获取到VIP客户号码则服务之
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号VIP客户服务");
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
try {//模拟正在为VIP客户服务
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//假如没获取到VIP客户号码,则调用普通窗口方法
System.out.println(windowName + "没有取到VIP任务!");
commonService();
}
}
}


5) 将用到的常量定义到一个类中 Constants类

定义三个常量:MAX_SERVICE_TIME、MIN_SERVICE_TIME、COMMON_CUSTOMER_INTERVAL_TIME

分别表示最大服务时间,最小服务时间,普通客户产生间隔

package com.itheima.bankqueque;

public class Constants
{
public static int MAX_SERVICE_TIME = 10000; //10秒!

public static int MIN_SERVICE_TIME = 1000; //1秒!

/* 为一个普通客户服务平均最快1秒钟完毕 ,假设间隔1秒钟产生一个普通客户*/
public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}


6) MainClass类

用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。

接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。

package com.itheima.bankqueque;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class MainClass {

private static Logger logger = Logger.getLogger("cn.itcast.bankqueue");

public static void main(String[] args) {
//产生4个普通窗口
for(int i=1;i<5;i++){
ServiceWindow window =  new ServiceWindow();
window.setNumber(i);
window.start();
}

//产生1个快速窗口
ServiceWindow expressWindow =  new ServiceWindow();
expressWindow.setType(CustomerType.EXPRESS);
expressWindow.start();

//产生1个VIP窗口
ServiceWindow vipWindow =  new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
vipWindow.start();

//普通客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//普通客户通过普通客户对应的号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getCommonManager().generateNewNumber();
/*
* 采用logger方式,无法看到直观的运行效果,因为logger.log方法内部并不是直接把内容打印出出来,
* 而是交给内部的一个线程去处理,所以,打印出来的结果在时间顺序上看起来很混乱。
**/
//logger.info("第" + serviceNumber + "号普通客户正在等待服务!");
System.out.println("第" + serviceNumber + "号普通客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME,
TimeUnit.SECONDS);

//快速客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//快速客户通过快速客户号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getExpressManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号快速客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2,
TimeUnit.SECONDS);

//VIP客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//VIP客户通过VIP客户号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getVipManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号VIP客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6,
TimeUnit.SECONDS);
}

}


执行结果:

第1号普通窗口开始获取普通任务!

第2号普通窗口开始获取普通任务!

第4号普通窗口开始获取普通任务!

第3号普通窗口开始获取普通任务!

第3号普通窗口没有取到普通任务,正在空闲一秒

第2号普通窗口没有取到普通任务,正在空闲一秒

第4号普通窗口没有取到普通任务,正在空闲一秒

第1号VIP窗口开始获取VIP任务!

第1号快速窗口开始获取快速任务!

第1号快速窗口没有取到快速任务!

第1号快速窗口开始获取普通任务!

第1号快速窗口没有取到普通任务,正在空闲一秒

第1号普通窗口没有取到普通任务,正在空闲一秒

第1号VIP窗口没有取到VIP任务!

第1号VIP窗口开始获取普通任务!

第1号VIP窗口没有取到普通任务,正在空闲一秒

第1号普通客户正在等待服务!

第1号快速客户正在等待服务!

第1号VIP客户正在等待服务!

第3号普通窗口开始获取普通任务!

第4号普通窗口开始获取普通任务!

第2号普通窗口开始获取普通任务!

第4号普通窗口没有取到普通任务,正在空闲一秒

第3号普通窗口开始为第1号普通客户服务

第2号普通窗口没有取到普通任务,正在空闲一秒

第1号快速窗口开始获取快速任务!

第1号快速窗口开始为第1号快速客户服务

第1号普通窗口开始获取普通任务!

第1号普通窗口没有取到普通任务,正在空闲一秒

第1号VIP窗口开始获取VIP任务!

第1号VIP窗口开始为第1号VIP客户服务

第2号普通客户正在等待服务!

第2号普通窗口开始获取普通任务!

第2号普通窗口开始为第2号普通客户服务

第4号普通窗口开始获取普通任务!

第4号普通窗口没有取到普通任务,正在空闲一秒

第1号快速窗口完成为第1号快速客户服务,总共耗时1秒

第1号普通窗口开始获取普通任务!

第1号普通窗口没有取到普通任务,正在空闲一秒
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: