1.需求:
模拟实现银行业务调度系统逻辑,具体需求如下:
银行业务调度系统模拟实现银行业务调度系统逻辑,具体需求如下:
银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,
快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号 )窗口没有客户等待办理业务的时候,
这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
2.示意图: (- -!原创)
3.源代码:
NumberManager:
package com.itheima.bankingsystem; import java.util.ArrayList; import java.util.List; /*号码管理器负责号码的生成和获取(返回)*/ public class NumberManager { private int number=1;//每个号码管理器生成号码都从1开始 private List<Integer> storeNumbers =new ArrayList<Integer>(); public synchronized int generateNumber(){//由客户去取生成的号码 storeNumbers.add(number); return number++; } public synchronized Integer fetchNumber(){//由服务窗口获取号码 if(storeNumbers.size()!=0) return storeNumbers.remove(0);//返回集合中第一个元素,队头元素保证FIFO else return null;//集合中可能没有元素 } /*窗口的取动作与客户的放动作在操作同一个集合,使用同步,这里的锁为它们对应的 同一个号码管理器实例*/ }NumberMachine:
package com.itheima.bankingsystem; /* 负责统一管理三个号码管理器,NumberMachine在程序运行期间只需要一个实例 设计成单例. */ public class NumberMachine { private NumberManager commonManager=new NumberManager(); private NumberManager vipManager=new NumberManager(); private NumberManager expressManager=new NumberManager(); public NumberManager getCommonManager(){//由于该类只有一个实例,因此成员commonManager的值不变 return commonManager; //也就是说多次调用getCommonManager()获取到的是同一个 } //NumberManager实例,下面同理 public NumberManager getVIPManager(){ return vipManager; } public NumberManager getExpressManager(){ return expressManager; } /*NumberMachine在程序运行时在内存限制只有一个实例*/ private NumberMachine(){} private static final NumberMachine ref=new NumberMachine(); public static NumberMachine getInstance(){ return ref; } }两个枚举:windowType和TimeConstant
package com.itheima.bankingsystem; public enum WindowType { COMMON,VIP,EXPRESS; @Override public String toString(){ switch(this){ case COMMON: return "普通"; case VIP: return "VIP"; case EXPRESS: return "快速"; default: return null; } } }package com.itheima.bankingsystem; //设置客户办理业务所需时间的最大值和最小值 public class TimeConstant { public static final int MIN_SERVICE_TIME=2;//服务最小时间 public static final int MAX_SERVICE_TIME=10;//服务最大时间 public static final int COMMON_CUSTOMER_INTERVAL_TIME=1;//每个普通用户来的时间间隔1秒 public static final int VIP_CUSTOMER_INTERVAL_TIME=6;//每个VIP用户来的时间间隔6秒 public static final int EXPRESS_CUSTOMER_INTERVAL_TIME=2;//每个快速用户来的时间间隔2秒 }ServiceWindow:
package com.itheima.bankingsystem; import java.util.Random; import java.util.concurrent.Executors; public class ServiceWindow { private WindowType windowType=WindowType.COMMON;//把三种窗口类型定义成枚举 private int windowID=1;//窗口的编号 public void setType(WindowType windowType) {//通过set方法设置窗口类型和窗口编号进行设置 this.windowType = windowType; //而没有通过构造方法在创建对象时传入 } //这样做可以修改已创建服务窗口对象的ID和type public void setWindowID(int windowID) { this.windowID = windowID; } public void start(){//相当于启动了NumberMachine,根据服务的不同 //获取不同号码管理器,然后在获取号码 Executors.newSingleThreadExecutor().execute( new Runnable(){ @Override public void run(){ while(true){//不断的获取服务窗口不断获取服务(fetchNumber) switch(windowType){ case COMMON:commonService();break; case VIP:vipService();break; case EXPRESS:expressService(); } } } } ); } public void commonService(){ String windowName=windowID+"号"+windowType+"窗口"; Integer serviceNumber=NumberMachine.getInstance().getCommonManager().fetchNumber();//服务窗口开始拿号,为该号客户提供服务 System.out.println(windowName+"正在获取任务"); if (serviceNumber!=null){ System.out.println(windowName+"正在为第"+serviceNumber+"个"+"普通客户服务"); int minServiceTime=TimeConstant.MIN_SERVICE_TIME; int maxServiceTime=TimeConstant.MAX_SERVICE_TIME; int serviceTime=new Random().nextInt(maxServiceTime-minServiceTime)+1+minServiceTime;//随机生成2~10秒 try { Thread.sleep(serviceTime*1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(windowName+"为第" +serviceNumber+"个"+"普通客户服务了"//这里没用windowType而固定使用"普通"字符串+serviceTime+"s"); //这是因为例如:在vipService方法中没有vip服务时,会调用commonService } //此时this.windowType=VIP,会错误打印VIP客户服务 else{ System.out.println(windowName+"窗口没有获取到"+"普通任务休息1秒钟再去获取"); try { Thread.sleep(1000);//服务窗口休息1秒后再去拿号 } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } public void vipService(){ String windowName=windowID+"号"+windowType+"窗口"; Integer serviceNumber=NumberMachine.getInstance().getVIPManager().fetchNumber();//服务窗口开始拿号,为该号客户提供服务 System.out.println(windowName+"正在获取任务"); if (serviceNumber!=null){//取到number,开始服务 System.out.println(windowName+"正在为第"+serviceNumber+"个"+windowType+"客户服务"); int minServiceTime=TimeConstant.MIN_SERVICE_TIME; int maxServiceTime=TimeConstant.MAX_SERVICE_TIME; int serviceTime=new Random().nextInt(maxServiceTime-minServiceTime)+1+minServiceTime;//随机生成2~10秒 try { Thread.sleep(serviceTime*1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(windowName+"为第" +serviceNumber+"个"+windowType+"客户服务了" +serviceTime+"s"); } else{ System.out.println(windowName+"窗口没有获取到"+windowType+"任务"); commonService();//VIP窗口没有获取到VIP任务,获取普通任务去服务,直接调用了 commonService(),去普通任务集合去取 } } public void expressService(){ String windowName=windowID+"号"+windowType+"窗口"; Integer serviceNumber=NumberMachine.getInstance().getExpressManager().fetchNumber();//服务窗口开始拿号,为该号客户提供服务 System.out.println(windowName+"正在获取任务"); if (serviceNumber!=null){//取到number,开始服务 System.out.println(windowName+"正在为第"+serviceNumber+"个"+windowType+"客户服务"); int serviceTime=TimeConstant.MIN_SERVICE_TIME; try { Thread.sleep(serviceTime*1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(windowName+"为第" +serviceNumber+"个"+windowType+"客户服务了" +serviceTime+"s"); } else{ System.out.println(windowName+"窗口没有获取到"+windowType+"任务"); commonService();//express窗口没有获取到express任务,获取普通任务去服务,直接调用了 commonService() } } }MainClass:
package com.itheima.bankingsystem; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class MainClass { public static void main(String[] args){ /*创建六个窗口对象(,1个VIP窗口,1个快速窗口)*/ //4个普通窗口 for(int i=1;i<5;++i){ ServiceWindow commonWindow=new ServiceWindow(); commonWindow.setWindowID(i); commonWindow.start();//开启服务 } //1个VIP窗口 ServiceWindow vipWindow=new ServiceWindow(); vipWindow.setType(WindowType.VIP); vipWindow.setWindowID(6); vipWindow.start(); //1个快速窗口 ServiceWindow expressWindow=new ServiceWindow(); expressWindow.setType(WindowType.EXPRESS); expressWindow.setWindowID(5); expressWindow.start(); /* 模拟客户来取 VIP客户 :普通客户 :快速客户 = 1 :6 :3 这个比例通过计时器来完成,假设每隔1秒来一个普通客户,每隔2秒来来一个快速客户. ,每隔六秒来一个VIP客户. 假设程序运行了12秒,来了12(12/1)个普通客户,6个快速客户(12/2),2个VIP客户(12/6) 2:12:6=1:6:3; */ Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ @Override public void run(){ int number=NumberMachine.getInstance().getCommonManager().generateNumber(); System.out.println("第"+number+"号普通客户等待服务"); } }, 1, TimeConstant.COMMON_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS); Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ @Override public void run(){ int number=NumberMachine.getInstance().getVIPManager().generateNumber(); System.out.println("第"+number+"号VIP客户等待服务"); } }, 1, TimeConstant.VIP_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS); Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable(){ @Override public void run(){ int number=NumberMachine.getInstance().getExpressManager().generateNumber(); System.out.println("第"+number+"号快速客户等待服务"); } }, 1, TimeConstant.EXPRESS_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS); } }运行结果可以通过输出到文本方式测试.
3.用模板方法设计模式改写ServiceWindow:
package com.itheima.bankingsystem; import java.util.Random; import java.util.concurrent.Executors; public class ServiceWindowNewCode { private WindowType windowType=WindowType.COMMON; private int windowID=1;//窗口的编号 String windowName=windowID+"号"+windowType+"窗口"; public void setType(WindowType windowType) { this.windowType = windowType; } public void setWindowID(int windowID) { this.windowID = windowID; } public void start(){ Executors.newSingleThreadExecutor().execute( new Runnable(){ @Override public void run(){ while(true){ Service();//默认为:普通窗口 } } } ); } public void Service(){ Integer serviceNumber=getServiceNumber();//不同的窗口对象会使用各自复写的getServiceNumber()方法(多态) System.out.println(windowName+"正在获取任务"); if (serviceNumber!=null){ System.out.println(windowName+"正在为第"+serviceNumber+"个"+"普通客户服务"); long serviceTime=getServiceTime(); try { Thread.sleep(serviceTime); } catch (InterruptedException e) { e.printStackTrace(); } } else notTakeTaskNext(); } /*不同类型的客户需要从不同的号码管理器获取ServiceNumber*/ protected int getServiceNumber(){ return NumberMachine.getInstance().getCommonManager().fetchNumber(); } /*VIP和COMMON服务时间在同一个范围内,EXPRESS为最小值*/ protected long getServiceTime(){ int minServiceTime=TimeConstant.MIN_SERVICE_TIME; int maxServiceTime=TimeConstant.MAX_SERVICE_TIME; int serviceTime=new Random().nextInt(maxServiceTime-minServiceTime)+1+minServiceTime;//随机生成2~10秒 return serviceTime*1000; } /*打印出哪个窗口正在为那个客户服务*/ protected void printServiceCustomer(){ System.out.println(windowName+"正在为第"+getServiceNumber()+"个"+"普通客户服务"); } /*打印服务了多长时间*/ protected void printServiceTime(){ System.out.println(windowName+"为第" +getServiceNumber()+"个"+"普通客户服务了" +getServiceTime()+"s"); } /*窗口未取到任务需要干的事*/ protected void notTakeTaskNext(){ System.out.println(windowName+"窗口没有获取到"+"普通任务休息1秒钟再去获取"); try { Thread.sleep(1000);//服务窗口休息1秒后再去拿号 } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }VIPServiceWindow
class VIPServiceWindow extends ServiceWindowNewCode{ private WindowType windowType=WindowType.VIP;//把三种窗口类型定义成枚举 protected int getServiceNumber(){//复写 return NumberMachine.getInstance().getVIPManager().fetchNumber(); } protected void printServiceTime(){ System.out.println(windowName+"为第" +getServiceNumber()+"个"+windowType+"客户服务了" +getServiceTime()+"s"); } protected void notTakeTaskNext(){//复写 System.out.println(windowName+"窗口没有获取到"+windowType+"任务"); new ServiceWindowNewCode().Service();//必须通过对象来调用普通窗口服务 } }ExpressServiceWindow
class ExpressServiceWindow extends ServiceWindowNewCode{ private WindowType windowType=WindowType.EXPRESS; protected int getServiceNumber(){//复写 return NumberMachine.getInstance().getExpressManager().fetchNumber(); } protected long getServiceTime(){ return TimeConstant.MIN_SERVICE_TIME; } protected void printServiceTime(){ System.out.println(windowName+"为第" +getServiceNumber()+"个"+windowType+"客户服务了" +getServiceTime()+"s"); } protected void notTakeTaskNext(){//复写 System.out.println(windowName+"窗口没有获取到"+windowType+"任务"); new ServiceWindowNewCode().Service();//必须通过对象来调用普通窗口服务 } }