银行业务调度系统

来源:互联网 发布:阿里云价格计算 编辑:程序博客网 时间:2024/05/20 05:07

面试技巧

Android面试技巧

项目经验:

例子:把苹果软件商店教育类游戏移植到Android平台

项目地址:www.xxx.com

面试时略微沉思,说得支支吾吾但是思路清晰

项目需求


面向对象的分析与设计

需求:
  • 有三种对应类型的客户:VIP客户,普通客户,快速客户 ,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务 。
分析:
  • 每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,我想到要有一个号码管理器对象,让这个对象不断地产生号码,就等于随机生成了客户。
  • 由于有三类客户,每类客户的号码编排都是完全独立的,所以,我想到本系统一共要产生三个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。
需求:
  • 各类型客户在其对应窗口按顺序依次办理业务 ,准确地说,应该是窗口依次叫号。
分析:
  • 各个窗口怎么知道该叫哪一个号了呢?它一定是问的相应的号码管理器,即服务窗口每次找号码管理器获取当前要被服务的号码。

类图


编写表示号码管理器的类

定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。
定义一个产生新号码的方法和获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,所以,要进行同步。
package com.shengyuan.interview.bank;import java.util.*;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 fetchServiceNumber(){ //返回值类型为Integer而不是int是因为当返回的是null时,int会发生空指针异常Integer number = null;if(queueNumber.size()>0)number = queueNumber.remove(0);return number;//return queueNumber.remove(0);//单独这句当没有元素的时候会发生数组角标越界异常}}
因为generateNewNumber和fetchServiceNumber是在各自独立线程中运行的方法,并且它们操作了共同数据,所以需要加上同步

编写表示号码机器的类

定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。
将NumberMachine类设计成单例。首先完成普通类,然后再完成单例
package com.shengyuan.interview.bank;public class NumberMachine {private NumberManager commonManager = new NumberManager();private NumberManager expressManager = new NumberManager();private NumberManager vipManager = new NumberManager();public NumberManager getCommonManager() {return commonManager;}public NumberManager getExpressManager() {return expressManager;}public NumberManager getVipManager() {return vipManager;}private NumberMachine(){}private static final NumberMachine instance = new NumberMachine(); public static NumberMachine getInstance(){return instance;}}

客户类型的枚举类

系统中有三种类型的客户,所以用定义一个枚举类,其中定义三个成员分别表示三种类型的客户。
重写toString方法,返回类型的中文名称。这是在后面编码时重构出来的,刚开始不用考虑。
package com.shengyuan.interview.bank;public enum CustomerType {COMMON,EXPRESS,VIP;//这里覆盖toString方法是为了在调用枚举实例时能够确保代码的一致性public String toString(){switch(this){case COMMON:return "普通";case EXPRESS:return "快速";case VIP:return name();}return null;//不写这句会报错,因为编译器不知道会不会执行到switch代码块中的语句}}

编写表示业务窗口的类

定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法,对三种不同类型的客户进行叫号。 
定义三个方法分别对三种客户进行服务,为了观察运行效果,应详细打印出其中的细节信息。
注意:switch比if else效率高
package com.shengyuan.interview.bank;import java.util.Random;import java.util.concurrent.Executors;public class ServiceWindow {//定义一个枚举变量,记录窗口类型,默认值是COMMONprivate CustomerType type = CustomerType.COMMON;//窗口号,默认值是1private int windowId = 1;//不采用构造方法而是用set方法是因为窗口的实例可能会改变,不要在初始化时就写死了。public void setType(CustomerType type) {this.type = type;}public void setWindowId(int windowId) {this.windowId = windowId;}//叫号的方法:start方法,每一个服务窗口里面有一个线程public void start(){Executors.newSingleThreadExecutor().execute(new Runnable(){public void run(){while(true){//需要不断叫号,所以无限循环。case后直接跟枚举字段,不需要加枚举类名!switch(type){case COMMON:commonService();break;case EXPRESS:expressService();break;case VIP:vipService();break;}}}});}private void commonService() {String windowName = "第"+windowId+"号"+type+"窗口";//这里的type直接调用toString方法,已被覆盖Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber();//fetchServiceNumber方法是阻塞方法,加了同步锁System.out.println(windowName+"正在获取任务!");if(number!=null){//叫到了号码,进行服务System.out.println(windowName+"正在为第"+number+"个"+"普通"+"客户"+"服务");//这里的”普通“是当vip或express窗口为普通客户服务时客户类型应为普通,如果直接写type就会返回vip或者express的客户类型了long beginTime = System.currentTimeMillis();int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;//maxRand = 9000long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//nextInt(num)返回一个0到num(不包括)之间的数try {Thread.sleep(serviceTime);} catch (InterruptedException e) {e.printStackTrace();}long costTime = System.currentTimeMillis()-beginTime;System.out.println(windowName+"为第"+number+"个"+"普通"+"客户"+"服务了"+costTime/1000+"秒");}else{System.out.println(windowName+"没有取到任务,先休息1秒钟!再叫号");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}private void expressService() {String windowName = "第"+windowId+"号"+type+"窗口";Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();System.out.println(windowName+"正在获取任务!");if(number!=null){//叫到了号码,进行服务System.out.println(windowName+"正在为第"+number+"个"+type+"客户"+"服务");long beginTime = System.currentTimeMillis();//int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;//maxRand = 9000//long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//nextInt(num)返回一个0到num(不包括)之间的数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();}}private void vipService() {String windowName = "第"+windowId+"号"+type+"窗口";//这里的type直接调用toString方法,已被覆盖Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber();System.out.println(windowName+"正在获取任务!");if(number!=null){//叫到了号码,进行服务System.out.println(windowName+"正在为第"+number+"个"+type+"客户"+"服务");long beginTime = System.currentTimeMillis();int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;//maxRand = 9000long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//nextInt(num)返回一个0到num(不包括)之间的数try {Thread.sleep(serviceTime);} catch (InterruptedException e) {e.printStackTrace();}long costTime = System.currentTimeMillis()-beginTime;System.out.println(windowName+"为第"+number+"个"+type+"客户"+"服务了"+costTime/1000+"秒");}else{System.out.println(windowName+"没有取到任务!");commonService();//vip窗口没有取到任务,不等待,紧接着调用普通服务,不用休息}}}
定义一个类Constants,用于定义使用的常量:服务时间的最大值和最小值
package com.shengyuan.interview.bank;public class Constants {public static final int MAX_SERVICE_TIME = 10000;public static final int MIN_SERVICE_TIME = 1000;}
代码的优化:ExpressWindow和VipWindow设计为CommonWindow的子类,最大限度重用代码

编写程序的主类和完成客户取号功能

在Constants类中加入了表示普通客户服务时间的常量:
package com.shengyuan.interview.bank;public class Constants {public static final int MAX_SERVICE_TIME = 10000;public static final int MIN_SERVICE_TIME = 1000;public static final int COMMON_CUSTOMER_INTERVAL_TIME = 1;}
主类:
用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。
package com.shengyuan.interview.bank;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class MainClass {public static void main(String[] args) {//创建4个普通窗口实例对象,每个窗口对象中各有一个独立运行的线程,并设置id,type为默认值,不用设置,最后启动叫号ServiceWindow commonWindow = null;for(int i=1;i<5;i++){commonWindow = new ServiceWindow();commonWindow.setWindowId(i);commonWindow.start();}//创建一个快速窗口实例对象,id为默认值1ServiceWindow expressWindow = new ServiceWindow();expressWindow.setType(CustomerType.EXPRESS);expressWindow.start();//创建一个vip窗口实例对象,id为默认值1ServiceWindow vipWindow = new ServiceWindow();vipWindow.setType(CustomerType.VIP);vipWindow.start();//模拟普通客户的到访,为单独的一个线程,这里不采用普通线程池,而采用调度线程池(也就是定时器线程)Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){//所谓来人就是叫号,首先找机器要号,下面两行代码应该同步,如果不加,会出现打印出窗口先服务,客户等待服务的情况(CPU执行完第一段代码后跳走了)Integer number = NumberMachine.getInstance().getCommonManager().generateNewNumber();System.out.println(number+"号"+CustomerType.COMMON.toString()+"客户等待服务!");}}, 0, //initialDelay:过多长时间开始执行命令(这里表示马上就来一个客户)Constants.COMMON_CUSTOMER_INTERVAL_TIME,//period,命令执行周期,可以用自定义常量TimeUnit.SECONDS//unit:时间单位);//模拟快速客户的到访Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){Integer number = NumberMachine.getInstance().getExpressManager().generateNewNumber();System.out.println(number+"号"+CustomerType.EXPRESS.toString()+"客户等待服务!");}}, 0, //initialDelay:过多长时间开始执行命令(这里表示马上就来一个客户)Constants.COMMON_CUSTOMER_INTERVAL_TIME*2,//period,命令执行周期,可以用自定义常量,在这里体现各类用户的概率比例TimeUnit.SECONDS//unit:时间单位);//模拟vip客户的到访Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){Integer number = NumberMachine.getInstance().getVipManager().generateNewNumber();System.out.println(number+"号"+CustomerType.VIP.toString()+"客户等待服务!");}}, 0, //initialDelay:过多长时间开始执行命令(这里表示马上就来一个客户)Constants.COMMON_CUSTOMER_INTERVAL_TIME*6,//period,命令执行周期,可以用自定义常量TimeUnit.SECONDS//unit:时间单位);}}
0 0
原创粉丝点击