奋斗黑马程序员----7k月薪面试题之银行业务调度系统

来源:互联网 发布:360数据恢复好用不 编辑:程序博客网 时间:2024/05/23 23:09
----------android培训java培训、期待与您交流! ----------

        银行业务调度系统

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

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

      Ø 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。

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

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

      Ø 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快

速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。

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

      Ø 当VIP6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。

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

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



面向对象的分析与设计

   在一个银行中,有三种对应类型的客户:VIP客户,普通客户,快速客户 ,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务 。

    首先,我们知道每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,我们要有一个号码管理器对象(NumberManager),让这个对象不断地产生号码,就等于随机生成了客户,如果有某个客户被服务,则将对应的号码取出,表示客户正在服务(用服务时间控制)。(谁拥有数据,谁就要提供操作数据的方法。有木有??

    由于有三类客户,每类客户的号码编排都是完全独立的,所以,本系统一共要产生三个号码管理器对象(VIP客户,普通客户,快速客户),各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器(NumberMachine)进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例(单例?想起来有木有?构造私有化....)。

   各类型客户在其对应窗口按顺序依次办理业务 ,准确地说,应该是窗口依次叫号。

    各个窗口怎么知道该叫哪一个号了呢?它一定是问的相应的号码管理器,即服务窗口每次找号码管理器获取当前要被服务的号码。(通过fetchServiceNumber()方法知道该叫哪个号)


对象分析:


NumberMachine,号码机器:管理三种类型窗口的号码,整个系统中只有一个,所以用单例实现( 单例模式要点??)每个窗口可以通过该类拿到对应的号码管理器对象,然后可以操作号码管理器。
     私有化三个成员变量:xxxManager,指向NumberManager对象,由于是单例模式,所以通过一个public的方法getXXXManager来
拿到每个窗口对应的号码管理器
     

NumberManager,管理器:不断的产生对应窗口的号码,并提供取出号码(可以理解为客户)的方法。
     其中:
  lastNumber = 1 -------------上一个客户的号码,初始化就代表第一个号码(客户)进来
  generateNewManager()--------产生号码,并用一个ArrayList集合给客户排队
  fetchServiceNumber()--------取出号码,总是取出最先等待的客户

ServiceWindow:服务窗口:
     里面有三种类型的窗口,ServiceWindow,expressService,vipService,当银行开始营业后(即,MainClass中调用对应的start方法,则每个窗口就开始工作(通过Executors.newSingleThreadExecutor().execute(...),开启一个单独的线程开始工作),为对应的客户解决问题。
     

CustomerType对象:存储客户类型,系统中只有三种客户,所以用一个枚举完成。
     ServiceWindow会通过该对象明确服务的客户类型。

            备注:普通窗口仅服务普通客户,而快速窗口和VIP窗口会优先服务其对应客户,当没有客户时,也解决普通客户的问题。


Constants对象:保存每个客户被服务的时间和三种类型各户访问的频率:
其中:
MAX_SERVICE_TIME = 10000---------------------最长服务时间,10秒
MIN_SERVICE_TIME = 1000 ---------------------最短服务时间,1秒(快速客户总是在最短的时间内解决问题)
COMMON_CUSTOMER_INTERVAL_TIME = 1------------普通客户生成的概率,1秒一个,可以在后面的程序中通过该变量的3倍,6倍表示快速客户和vip客户生成的概率。


MainClass对象:
初始化各个窗口,其中普通窗口4个,1个快速,1个vip,调用对应的start()方法,让各个窗口开始工作。
开启三个线程池,Executors.newScheduledThreadPool(1).scheduleAtFixedRate(),1秒后开始执行定时器任务(通过scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 完成),每隔n秒就产生一个客户。


代码实现:

NumberMachine,号码机器

/**号码产生器, * 拿到三个号码管理器:vip,快速,普通 * 谁拥有数据,谁就要提供操作数据的方法。 */public class NumberMachine {private NumberManager commonManager = new NumberManager();private NumberManager expressManager = new NumberManager();private NumberManager vipManager = new NumberManager();/** * @return 返回值类型是NumberManger */public NumberManager getCommonManager(){return commonManager;}/** * @return 返回值类型是NumberManger */public NumberManager getExpressManager(){return expressManager;}/** * @return 返回值类型是NumberManger */public NumberManager getVipManager(){return vipManager;}/* * 把对象变成单例(单例是重点,一定要小心) * 流程如下,把构造函数私有化, */private NumberMachine(){}/** * @return 返回值类型是NumberMachine */public static NumberMachine getInstance(){return instance;}private static NumberMachine instance = new NumberMachine();}


NumberManager,管理器
此处没有导包..
/** * 号码管理器:负责产生号码,并提供一个供各个窗口叫号的方法 */public class NumberManager {private int lastNumber = 1;private List<Integer> queueNumber = new ArrayList<Integer>();/* * 问题:左边为什么不用ArrayList? * 因为此处使用面向接口编程, 如果以后要修改代码,将右边的ArrayList换成LinkedList, * 就很方便。变量的类型尽量用面向父级,或者上一级,这样更灵活。 *//** * 号码产生器,得到排队的号码 *  * 需要加同步,该方法和下面的方法都操作了queueNumber。 * @return */public synchronized Integer generateNewManager(){/* * 如果集合中为空,那么返回值为null,是不能转int类型的,所以将int换成Integer,自动 * 拆箱和装箱,就会避免 */queueNumber.add(lastNumber);return lastNumber++;}/** * 取号码的方法,得到最早进入的,正在排队的号码 * 但是有一种情况,如果排队的号码到了160,而服务的号码才到了70, * 所以此时不能直接返回新产生的号码 *  * 需要加同步,该方法和上面的方法都操作了queueNumber。 * @return 集合中最前的号码 */public synchronized Integer fetchServiceNumber(){/* * 如果集合中为空,那么返回值为null,是不能转int类型的,所以将int换成Integer,自动 * 拆箱和装箱,就会避免 */Integer number = null;if(queueNumber.size()>0){number = queueNumber.remove(0);//总是移除首位的客户}return number;}}


ServiceWindow:服务窗口
此处没有导包..
/**服务窗口 *  */public class ServiceWindow {private CustomerType type = CustomerType.COMMON;//默认客户类型是普通客户private int windowId = 1;//默认的窗口id是1//private static int j = 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(){//System.out.println("第"+j+++"个线程开始干活");/** * 该循环一旦开始,就不会再停下, 知道报异常 */while(true){/* switch比if else效率高,为什么?if(type == CustomerType.COMMON){NumberMachine.getInstance().getCommonManager();}*///一道面试题:switch数据只能是哪些?int可以,long不行,枚举可以switch(type){case COMMON:commonService();break;case EXPRESS:expressService();break;case VIP:vipService();break;}}}});}private void commonService() {String windowName = "第" + windowId + "号" + type + "窗口";System.out.println(windowName + "正在获取普通任务ing");//用number变量拿到要被服务的号码Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber();//取出号码if(number != null){System.out.println(windowName + "为第" + number + "个普通客户服务");long beginTime = System.currentTimeMillis();//窗口休息时间int maxServerTime = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;long serveTime = new Random().nextInt(maxServerTime)+1+Constants.MIN_SERVICE_TIME;try{Thread.sleep(serveTime);//让该线程睡眠,表示正在被服务}catch(InterruptedException e){e.printStackTrace();}long costTime = (System.currentTimeMillis()-beginTime)/1000;System.out.println(windowName + "已经为第" + number + "个普通客户完成服务,耗时" + costTime);}else{System.out.println(windowName + "没有取到普通任务,先休息一秒钟");//没有取到任务,休息1stry{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}}}private void expressService() {String windowName = "第" + windowId + "号" + type + "窗口";System.out.println(windowName + "正在等待express客户");Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();if(number != null){System.out.println(windowName + "为第" + number + "个" + type + "客户服务");long beginTime = System.currentTimeMillis();//窗口休息时间//int maxServerTime = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;//long serveTime = new Random().nextInt(maxServerTime)+1+Constants.MIN_SERVICE_TIME;try{Thread.sleep(Constants.MIN_SERVICE_TIME);//快速窗口休息时间只能是最小值}catch(InterruptedException e){e.printStackTrace();}long costTime = (System.currentTimeMillis()-beginTime)/1000;System.out.println(windowName+ "已经为第" + number + "个" + type + "客户完成服务,耗时" + costTime);}else{System.out.println(windowName + "没有取到快速任务");//快速窗口没有取到快速任务后,就去普通任务commonService();}}private void vipService() {String windowName = "第"+ windowId + "号"+type+"窗口";System.out.println(windowName + "现在空闲,等待vip客户咨询。");Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber();if(number != null){System.out.println(windowName + "为第" + number + "个" + type + "客户服务");long beginTime = System.currentTimeMillis();//窗口休息时间int maxServerTime = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;long serveTime = new Random().nextInt(maxServerTime)+1+Constants.MIN_SERVICE_TIME;try{Thread.sleep(serveTime);}catch(InterruptedException e){e.printStackTrace();}long costTime = (System.currentTimeMillis()-beginTime)/1000;System.out.println(windowName + "已经为第" + number + "个" + type + "客户完成服务,耗时" + costTime);}else{System.out.println(windowName+"没有取到vip任务");//没有取到vip任务后,不是先休息,而是去取普通任务commonService();}}}


CustomerType,客户类型对象

/**由于客户的类型是固定的, 只能是普通,快速,vip中的一种,所以用用一个枚举类 * 来设计。 *  */public enum CustomerType {COMMON,EXPRESS,VIP;public String toString(){switch(this){case COMMON:return "普通";case EXPRESS:return "快速";case VIP:return name();/* *  String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明。  */}return null;}}


Constants对象

/** * 服务的时间最大值与最小值,1s~10s */public class Constants {public static int MAX_SERVICE_TIME = 10000;public static int MIN_SERVICE_TIME = 1000;public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;}


MainClass对象
此处没有导包..
public class MainClass {/** * @param args */public static void main(String[] args) {/* * 创建4个普通客户窗口 */for(int i=1;i<5;i++){ServiceWindow commonWindow = new ServiceWindow();commonWindow.setWindowId(i);commonWindow.start();}//创建快速客户窗口ServiceWindow expressWindow = new ServiceWindow();expressWindow.setType(CustomerType.EXPRESS);expressWindow.setWindowId(5);expressWindow.start();//生成vip窗口ServiceWindow vipWindow = new ServiceWindow();vipWindow.setType(CustomerType.VIP);vipWindow.setWindowId(6);vipWindow.start();/* * static ScheduledExecutorService  newScheduledThreadPool(int corePoolSize)  * 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。  *  *  ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, *  long period, TimeUnit unit)  *  创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay  *  后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。  *//** * 这三个线程就是负责创建客户,并打印出客户正在等待的信息 */Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){Integer number = NumberMachine.getInstance().getCommonManager().generateNewManager();System.out.println("普通客户"+number+"号正在等待服务");}},0, Constants.COMMON_CUSTOMER_INTERVAL_TIME, //每隔一秒,就运行一次该线程,就可以创建一个客户TimeUnit.SECONDS);Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager();System.out.println("Express客户"+number+"号等待服务!");}},0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 3, //快速客户,3秒来一个TimeUnit.SECONDS);Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){Integer number = NumberMachine.getInstance().getVipManager().generateNewManager();System.out.println("vip客户"+number+"号正在等待服务!");}},0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, //vip客户,6秒来一个TimeUnit.SECONDS);}}


部分模拟结果:

第2号普通窗口正在获取普通任务ing
第2号普通窗口没有取到普通任务,先休息一秒钟
第4号普通窗口正在获取普通任务ing
第4号普通窗口没有取到普通任务,先休息一秒钟
第6号VIP窗口现在空闲,等待vip客户咨询。
第6号VIP窗口没有取到vip任务
第6号VIP窗口正在获取普通任务ing
第6号VIP窗口没有取到普通任务,先休息一秒钟
第1号普通窗口正在获取普通任务ing
第1号普通窗口没有取到普通任务,先休息一秒钟
第3号普通窗口正在获取普通任务ing
第3号普通窗口没有取到普通任务,先休息一秒钟
第5号快速窗口正在等待express客户
第5号快速窗口没有取到快速任务
第5号快速窗口正在获取普通任务ing
第5号快速窗口没有取到普通任务,先休息一秒钟
普通客户1号正在等待服务
vip客户1号正在等待服务!
Express客户1号等待服务!
普通客户2号正在等待服务
第2号普通窗口正在获取普通任务ing
第6号VIP窗口现在空闲,等待vip客户咨询。
第6号VIP窗口为第1个VIP客户服务
第4号普通窗口正在获取普通任务ing
第4号普通窗口为第1个普通客户服务
第2号普通窗口为第2个普通客户服务
第1号普通窗口正在获取普通任务ing
.........

----------android培训java培训、期待与您交流!----------

  详细请查看:http://edu.csdn.net/heima/

原创粉丝点击