java7K面试题-----------银行业务调度系统 (转)
一、银行业务需求
模拟实现银行业务调度系统逻辑,具体需求如下:
1、银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
2、有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
3、异步随机生成各种类型的客户,生成各类型用户的概率比例为:VIP客户 :普通客户 :快速客户 = 1 :6 :3。
4、客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,
快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
5、各类型客户在其对应窗口按顺序依次办理业务。
6、当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,
而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
7、随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
8、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
二、面向对象的设计与分析
1、有三种对应类型的客户:VIP客户,普通客户,快速客户,异步随机生成各种类型的客户。
1.每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以要有一个号码管理器对象,
让这个对象不断地产生号码,就等于随机生成了客户。
2.由于有三类客户,每类客户的号码编排都是完全独立的,所以本系统一共要产生三个号码管理器对象,
各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,
这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。
2、各类型客户在其对应窗口按顺序依次办理业务 ,准确地说,应该是窗口依次叫号。
1.各个窗口内部有相应的号码管理器方法,即服务窗口每次找号码管理器获取当前要被服务的号码。
2.各类客户只是拿着号码等待被叫。
三、对象分析类图
四、代码实现
1、NumberManager类
1. 定义一个号码产生器,对外提供2个方法:
generateNumber();方法是给线程调用随机产生号码,模拟客户取了号码等待办理业务
fetchNumber();方法是给银行窗口获取正在等待被服务的客户对应的号码
2. 该类中还需要一个容器,用于临时存储随机产生的号码,要求号码形成队列结构
用到:ArrayList集合。
并且两个方法都是对同一集合的数据进行操作,需要同步synchroniaed
NumberManager代码实现:
package cn.the_bank; import java.util.ArrayList; import java.util.List; //号码产生器 public class NumberManager { //定义一个用于记住号码的变量 private int lastNumber=1; //创建一个集合用于存储产生号码的队列集合 private List<Integer> queueNumber=new ArrayList<Integer>(); //添加和取出的数据同属于一个集合中,所以需要同步 //将产生的号码添加进集合 public synchronized Integer generateNewNumber(){ queueNumber.add(lastNumber); return lastNumber++; } //服务窗口获取号码的方法 public synchronized Integer fetchServiceNunmber(){ Integer number=null; //判断,集合中是否有数据,有,就删除第一个元素并且返回 if(queueNumber.size()>0){ return queueNumber.remove(0); } return number; } }
2、定义一个号码管理器:NumberMachine对象
1. 在该类中创建3个号码产生器对象(NumberManager),分别对应3个类型的客户,
并且对外提供获取这3个对象的方法
2. 分析号码管理器始终是一个;相当于生活中银行里的取号机,所以将NumberMachine类设计
成单例,并且对外提供静态的的方法获取本类对象,私有构造方法
NumberMachine类代码:
package cn.the_bank; //号码管理器 public class NumberMachine { private NumberManager commonManager=new NumberManager(); private NumberManager expresManager=new NumberManager(); private NumberManager vipManager=new NumberManager(); public NumberManager getCommonManager() { return commonManager; } public NumberManager getExpressManager() { return expresManager; } public NumberManager getVipManager() { return vipManager; } //私有构造方法 private NumberMachine(){} //对外提供获取号码管理器对象方法 public static NumberMachine getInstance(){ return instance; } //创建本类对象 private static NumberMachine instance=new NumberMachine(); }
3、定义一个窗口为客户服务的时间类(Constants类),定义成静态常量
package cn.the_bank; //定义常量,表示服务的时间 public class Constants { //服务的最大时间 public static int MAX_SERVICE_TIME=10000; //服务的最小时间 public static int MIN_SERVICE_TIME=1000; //该常量用于定时器,客户号码产生的时间间隔频率 public static int COMMON_CUSTOMER_INTERVAL_TYME=1; }
4、定义一个客户类型类:CustomerType类
3种客户类型,所以以枚举的方式定义,并且复写toString()方法
package cn.the_bank; public enum CustomerType { COMMON,EXPRESS,VIP; public String toString(){ String name=null; switch(this){ case COMMON: return "普通"; case EXPRESS: return "快速"; case VIP: return name(); } return null; } }
5、定义一个服务窗口类:ServiceWindow类
1.提供一个开启服务窗口开启的方法,通过线程池线程方式来对3个类型的客户提供3个不同的
服务方式:普通客户的服务;快速客户的服务;VIP客户服务
用上switch语句
2.类中定义一个CustomerType类型的变量,记住枚举类返回的类型客户
提供设置客户类型的方法
3. 类中定义一个 int类型的变量,记住窗口号码
提供设置窗口号码的方法
package cn.the_bank; import java.util.Random; import java.util.concurrent.Executors; //服务窗口,明确客户类型,启动服务,服务耗时, public class ServiceWindow { //定义客户类型变量,初始化为普通客户类型 private CustomerType type=CustomerType.COMMON; //定义窗口变量,从1开始 private int windowId=1; //设置客户类型方法 public void setType(CustomerType type) { this.type = type; } //设置窗口号码的方法 public void setWindowId(int windowId) { this.windowId = windowId; } //启动服务,开始叫号 public void start(){ //线程控制,从获取任务到接收任务 Executors.newSingleThreadExecutor().execute(new Runnable(){ public void run(){ while(true){ switch(type){ case COMMON: commonService(); break; case EXPRESS: expressService(); break; case VIP: VIPService(); break; } } } }); } //将代码提取,封装成一个函数,对普通客户服务的窗口 public void commonService(){ String windowName=windowId+"号"+type+"窗口"; //通过获取号码管理器对象,获取服务客户的号码 Integer number=NumberMachine.getInstance().getCommonManager().fetchServiceNunmber(); System.out.println(windowName+"........正在获取普通任务"); //如果取得被服务的客户号码,就进行服务 if(number!=null){ System.out.println(windowName+"为第"+number+"个=======================普通客户服务"); long beginTime=System.currentTimeMillis(); int maxRand=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME; long serviceTime=new Random().nextInt(maxRand)+1+Constants.MAX_SERVICE_TIME; try { Thread.sleep(serviceTime); } catch (InterruptedException e) { e.printStackTrace(); } long costTime=System.currentTimeMillis()-beginTime; System.out.println(windowName+"为第"+number+"个普通客户服务,耗时时间"+(costTime)/1000); } //如果没有取得被服务的客户的号码,就休息1秒 else{ System.out.println(windowName+"没有取到普通任务,休息一秒!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //System.out.println("-----------------------------------------------"); } //快速窗口服务函数 public void expressService(){ String windowName=windowId+"号"+type+"窗口"; //通过获取号码管理器对象,获取服务客户的号码 Integer number=NumberMachine.getInstance().getExpressManager().fetchServiceNunmber(); System.out.println(windowName+".......正在获取任务"); //如果取得被服务的客户号码,就进行服务 if(number!=null){ System.out.println(windowName+"为第"+number+"个====================快速客户服务"); //开始服务时间 long beginTime=System.currentTimeMillis(); //服务时间应该为最少时间Constants.MIN_SERVICE_TIME try { Thread.sleep(Constants.MIN_SERVICE_TIME); } catch (InterruptedException e) { e.printStackTrace(); } long costTime=System.currentTimeMillis()-beginTime; System.out.println(windowName+"为第"+number+"个"+type+"客户服务,耗时时间"+(costTime)/1000); } //如果没有取得被服务的客户的号码,就去普通任务的号码 else{ System.out.println(windowName+"没有取得快速任务"); commonService(); } //System.out.println("-----------------------------------------------"); } //为VIP客户服务的函数 public void VIPService() { String windowName=windowId+"号"+type+"窗口"; //通过获取号码管理器对象,获取服务客户的号码 Integer number=NumberMachine.getInstance().getVipManager().fetchServiceNunmber(); System.out.println(windowName+"..........正在获取任务"); //如果取得被服务的客户号码,就进行服务 if(number!=null){ System.out.println(windowName+"为第"+number+"个===================VIP客户服务"); //开始服务时间 long beginTime=System.currentTimeMillis(); //服务时间应该为最大时间减去最小时间 int maxRandom=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME; //随机从maxRandom中获取服务时间,再加上最小服务时间 long serviceTime=new Random().nextInt(maxRandom+1)+Constants.MIN_SERVICE_TIME; try { Thread.sleep(serviceTime); } catch (InterruptedException e) { e.printStackTrace(); } long costTime=System.currentTimeMillis()-beginTime; System.out.println(windowName+"为第"+number+"个"+type+"客户服务,耗时时间"+(costTime)/1000); } //如果没有取得被服务VIP客户的号码,就去取普通任务的号码 else{ System.out.println(windowName+"没有取得VIP任务"); commonService(); } //System.out.println("-----------------------------------------------"); } }
6、定义MainClass 类作为程序入口,并且模拟客户调用号码管理器中
的号码产生器中的生成客户排队号码,通过定时器固定频率定时产生客户号码
并且创建窗口,开启服务窗口
实例代码:
package cn.the_bank; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class MainClass { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //通过循环创建4个普通窗口并开启服务 for(int i=1;i<5;i++) { //创建普通窗口 ServiceWindow commonWindow=new ServiceWindow(); commonWindow.setWindowId(i); //开启线程并进行服务 commonWindow.start(); } //创建5号快速窗口,并开启服务 ServiceWindow expressWindow=new ServiceWindow(); expressWindow.setType(CustomerType.EXPRESS); expressWindow.setWindowId(5); expressWindow.start(); //创建6号VIP窗口,并开启服务 ServiceWindow VIPWindow=new ServiceWindow(); VIPWindow.setType(CustomerType.VIP); VIPWindow.setWindowId(6); VIPWindow.start(); //通过定时器思想,普通客户定时循环取号 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ public void run(){ Integer number=NumberMachine.getInstance().getCommonManager().generateNewNumber(); //为了更好的查看代码的运行状态 System.out.println("普通客户:第<"+number+">号正在等待服务"); } }, 0, Constants.COMMON_CUSTOMER_INTERVAL_TYME, TimeUnit.SECONDS); //快速客户定时取号 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ public void run(){ Integer number=NumberMachine.getInstance().getExpressManager().generateNewNumber(); System.out.println("快速客户第:<"+number+">号正在等待服务"); } }, 0, Constants.COMMON_CUSTOMER_INTERVAL_TYME*2, TimeUnit.SECONDS); //VIP客户定时取号 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ public void run(){ Integer number=NumberMachine.getInstance().getVipManager().generateNewNumber(); System.out.println("VIP客户:第<"+number+">号正在等待服务"); } }, 0, Constants.COMMON_CUSTOMER_INTERVAL_TYME*6, TimeUnit.SECONDS); } }