------- android培训、java培训、期待与您交流! ----------
银行业务调度系统具体需求:
银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
简单的分析:
这个题目和上一个交通灯管理系统的题目对比,更加复杂一些,因为其中有不同类型的客户,使得不能简单的定义一个集合进行获取操作。试想这里窗口可以自己实现叫号,它知道自己所具有的的属性,所以不需要定义这么一个是否有人的状态变量。下面是我看视频的一些总结
面向对象的分析与设计
1,有三种客户:VIP客户、普通客户、快速客户。需要异步随机产生各种类型的客户。
(1)每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,要有一个号码管理器对象,让这个对象不断地产生号码,就等于随机生成了客户。
(2)由于有三类客户,每类客户的号码编排都是完全独立的,所以,本系统一共要产生三个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。
2,各类客户到对应的窗口办理业务,是窗口在叫号
(1)窗口内部知道如何叫号,是通过号码机器获取的号,所以它内部应该有这个叫号的方法
(2)各类客户并不知道怎么操作叫号的方法,只是等待被叫
张老师分析类图:
编写NumberManager类:实现取号、叫号功能
定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。
定义一个产生新号码的方法和获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,所以,要进行同步。
- <span style="font-size:12px;">package com.itheima.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 fetchServiceNumber(){
- Integer number=null;
- //如果有正在等待的客户则服务
- if (queueNumber.size()>0) {
- return queueNumber.remove(0);
- }
- return number;
- }
- } </span>
编写NumberMachine类
定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。
这个号码机器在整个系统中始终只能有一个,将NumberMachine类设计成单例。
- package com.itheima.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(){};
- //只能调用静态方法
- public static NumberMachine getInstance() {
- return instance;
- }
- //自己创建返回的对象
- private static NumberMachine instance = new NumberMachine();
- //如果有正在等待的客户则服务
- if (queueNumber.size()>0) {
- return queueNumber.remove(0);
- }
- return number;
- }
- }
CustomerType枚举类:使用枚举列出客户类型,显得更加专业
系统中有三种类型的客户,所以定义一个枚举类,其中定义三个成员分别表示三种类型的客户。
- package com.itheima.bank;
- public enum CustomerType {
- COMMON,EXPRESS,VIP;
- //覆盖toString方法,以便调用时用汉字输出更直观
- public String toString(){
- String name=null;
- switch(this){
- case COMMON:
- name="普通";
- break;
- case EXPRESS:
- name="快速";
- break;
- case VIP:
- name=name();
- break;
- }
- return name;
- }
- }
编写ServiceWindow类
定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法。
定义三个方法分别对三种客户进行服务
- package com.itheima.bank;
- import java.util.Random;
- import java.util.concurrent.Executors;
- public class ServiceWindow {
- //定义客户类型变量
- private CustomerType type = CustomerType.COMMON;
- //定义服务窗口变量
- 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() {
- switch(type) {
- case COMMON:
- //确定客户类型后,不停的叫号、服务
- while(true)
- commonService();
- case EXPRESS:
- while(true)
- expressService();
- case VIP:
- while(true)
- vipService();
- }
- }
- });
- }
- //为普通客户服务
- private void commonService() {
- String windowName = "第"+windowId+"号"+type+"窗口";
- //从号码管理器中获取正在等待服务的客户
- Integer number=NumberMachine.getInstance().getCommonManager().fetchServiceNumber();
- System.out.println(windowName+"正在获取普通任务");
- //如果有正在等待服务的客户
- if (number!=null) {
- System.out.println(windowName+"为第"+number+"个普通客户服务");
- //开始服务的时间
- 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+"个普通客户完成服务,耗时"+costTime/1000+"秒");
- //如果没有正在等待服务的普通客户,就休息1秒
- } 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+"个快速客户服务");
- //开始服务的时间
- long beginTime=System.currentTimeMillis();
- try {
- //快速客户服务时间为最小值
- Thread.sleep(Constants.MIN_SERVICE_TIME);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //为客户服务的花费的时间
- long costTime=System.currentTimeMillis()-beginTime;
- System.out.println(windowName+"为第"+number+"个快速客户完成服务,耗时"+costTime/1000+"秒");
- } else {
- //如果没有正在等待服务的快速客户,就为普通客户服务
- System.out.println(windowName+"没有获取到快速任务");
- commonService();
- }
- }
- //为vip客户服务
- private void vipService() {
- String windowName="第"+windowId+"号"+type+"窗口";
- //从号码管理器中获取正在等待服务的客户
- Integer number=NumberMachine.getInstance().getVipManager().fetchServiceNumber();
- System.out.println(windowName+"正在获取vip任务");
- //如果有正在等待服务的客户
- 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+"个VIP客户完成服务,耗时"+costTime/1000+"秒");
- } else {
- //如果没有正在等待服务的vip客户,则为普通客户服务
- System.out.println(windowName+"没有获取到VIP任务");
- commonService();
- }
- }
- }
编写Constants类:等待时间为常量
定义三个常量:MAX_SERVICE_TIME、MIN_SERVICE_TIME、COMMON_CUSTOMER_INTERVAL_TIME
- package it.cast.bank;
- 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类
用for循环创建出4个普通窗口,再单独创建出1个快速窗口(5号)和一个VIP窗口(6号)。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。
- package com.itheima.bank;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- public class MainClass {
- public static void main(String[] args) {
- //产生4个普通窗口,并开始服务
- for (int i = 1; i < 5; i++) {
- ServiceWindow commonWindow = new ServiceWindow();
- commonWindow.setWindowId(i);
- commonWindow.start();
- }
- //产生5号快速窗口,并开始服务
- ServiceWindow expressWindow=new ServiceWindow();
- expressWindow.setWindowId(5);
- expressWindow.setType(CustomerType.EXPRESS);
- expressWindow.start();
- //产生6号vip窗口,并开始服务
- ServiceWindow vipWindow=new ServiceWindow();
- vipWindow.setWindowId(6);
- vipWindow.setType(CustomerType.VIP);
- vipWindow.start();
- //普通客户定时取号 ,应用1.5新特性创造线程
- Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
- new Runnable() {
- public void run() {
- Integer number=NumberMachine.getInstance().getCommonManager().generateNewNumber();
- 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().generateNewNumber();
- System.out.println(number+"号快速客户正在等待服务!");
- }
- },
- 0,
- Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2,
- TimeUnit.SECONDS);
- //vip客户定时取号
- Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
- new Runnable() {
- public void run() {
- Integer number=NumberMachine.getInstance().getVipManager().generateNewNumber();
- System.out.println(number+"号vip客户正在等待服务!");
- }
- },
- 0,
- Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6,
- TimeUnit.SECONDS);
- }
- }
定义一个号码管理的类,实现取号、叫号功能;号码机器,实现了3中不同类型的号码,并采用单例设计模式(复习了单例);使用枚举列出客户类型,显得更加专业同时也复习了枚举;窗口是代码最复杂的类,不过思维确不复杂,只要不断叫号,叫到号之后等待一段时间。这种思维的体现形式十分特别,面向对象的思维是多元化的,不存在固定的套路,只要能实现功能,并且代码简单都是好程序,所以发散思维尤为重要,不能陷入僵化的思维模式。
这个系统使用多次线程池,需要注意这个新特新。要记住了Executors这个关键的类,如此类使用的newScheduledThreadPool(1).scheduleAtFixedRate()就是一个时间控制器,每隔固定的时间执行一次(炸八角大楼),不需要我们再用sleep套上循环来使用,十分简单与方便,又学习一个简便方法。