转载请申明出处:http://blog.csdn.net/xmxkf
1. 银行调度业务系统的题目来源与需求阐述
银行业务调度系统:
模拟实现银行业务调度系统逻辑,具体需求如下:
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方式展现程序运行结果。
2 对银行调度业务系统进行面向对象分析设计
面向对象的分析与设计:
1、有三种对应类型的客户:VIP客户,普通客户,快速客户;异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务。
A、 首先,经常在银行办理业务的人更有利于理解本系统。每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的,所以,我们想要有一个号码管理器对象,让这个对象不断的产生号码,就等于随机生成了客户;
B、 由于有三种类型客户,每类客户的号码编排都是完全独立的,所以,我想到本系统一共要产生3个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。
2、各类型客户在其对应窗口按顺序依次办理业务,准确的说,应该是窗口依次叫号;
A、各个窗口怎么知道该叫那一个号了呢?它一定是问的对应的号码管理器,即服务窗口每次找号码管理器获取当前要被服务的号码;
B、如果不是多次亲身经历银行的这种业务,再加上积累大量的面向对象开发的应用经验,不可能轻松的进行这种设计,能否发掘出其中隐含的对象信息,还要看日积月累出来的感觉。
画一个类图进行分析:(画图有助于理解和分析问题)
以下为源代码:(见相应注释)
3. 编写表示号码管理器的类
import java.util.ArrayList;
import java.util.List;
/**
* NumberManager:号码管理器(可理解为银行挂号机器中的一个分支)
*
当客户来了要生成一个号码,并加在对应客户类型的集合中;
*
服务窗口会找对应的取号机取号去服务。
*
因为生成号码和取走号码同时用到成员变量,但又是不同的线程,需要加同步
*/
publicclass NumberManager
{
privateintlastNumber = 1; //记住上一个号码
private List<Integer>queueNumber =new
ArrayList<Integer>();
//客户来了生成号码
publicsynchronized Integer generateNewManager()
{
//来一个客户就往集合中加一个号码
queueNumber.add(lastNumber);
returnlastNumber++;
}
//服务窗口获取号码
publicsynchronized Integer fetchServiceNumber()
{
Integer number =
null;
if(queueNumber.size()>0)
{
//获取集合中第一个号码,因为第一个号码是最先加进来并且没取走的
number =
queueNumber.remove(0);
}
return number;
}
}
4. 编写表示号码机器的类
/**
* NumberMachine:号码机器(相当于我们去银行用的挂号机器)
*
会返回三个号码管理器;
*
号码机器在系统中只能有一个,要不然就会有几个相同的号码机器,窗口不知道去哪个号码机上拿号码
*/
publicclass NumberMachine
{
//三种类型的客户(普通,快速,vip)
private NumberManagercommonManager =new
NumberManager();
private NumberManagerexpressManager =new
NumberManager();
private NumberManagervipManager =
new NumberManager();
//get方法
public NumberManager getCommonManager() {
returncommonManager;
}
public NumberManager getExpressManager() {
returnexpressManager;
}
public NumberManager getVipManager() {
returnvipManager;
}
//单例设计模式中构造方法私有
private NumberMachine(){}
//单例设计模式,返回一个对象
publicstatic NumberMachine getInstance()
{
returninstance;
}
privatestatic NumberMachineinstance
=new NumberMachine();;
}
5. 编写表示业务窗口的类的骨架代码
/**
*
客户类型(枚举):CustomerType
*/
publicenum CustomerType
{
COMMON,EXPRESS,VIP;
public String toString()
{
switch(this)
{
caseCOMMON:
return"普通";
caseEXPRESS:
return"快速";
caseVIP:
return name();
}
returnnull;
}
}
6. 完成表示业务窗口的类的细节代码
import java.util.Random;
import java.util.concurrent.Executors;
/**
*
服务窗口类:ServiceWindow
*
有两个属性,一个是窗口类型,一个是窗口号
*/
publicclass ServiceWindow
{
//代表窗口类型(3种)枚举。默认普通,因为普通最多
private CustomerTypetype= CustomerType.COMMON;
privateintwindowId = 1; //窗口号
//为什么只要get方法,因为以后可以设置窗口类型及号码,不要一修了窗口以后就不能更改了
publicvoid setType(CustomerType type) {
this.type = type;
}
publicvoid setWindowId(int
windowId) {
this.windowId = windowId;
}
publicvoid start()
{
Executors.newSingleThreadExecutor().execute(new Runnable(){
publicvoid run()
{
while(true)
{
/*if(type==CustomerType.COMMON)
NumberMachine.getInstance().getCommonManager();
else if()
else if()*/
//此处用switch更高效
switch(type) //表达式中可放的类型byte
shortint char enum
{
//向号码管理器要号码,也就是要服务
caseCOMMON:
commonService();
break;
caseEXPRESS:
expressService();
break;
caseVIP:
vipService();
break;
}
}
}
});
}
//为普通客户服务
privatevoid commonService() {
//此处不能改为普通。因为VIP和快速窗口没任务是会来为普通服务
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 maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
//服务时间1000-10000毫秒
long serveTime =new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
try {
//线程睡几秒就好像为客户服务的时间
Thread.sleep(serveTime);
} 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();
}
}
}
//为快速客户服务
privatevoid 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;
//long serveTime = new Random().nextInt(maxRand)+1+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+"没有取到任务!");
//快速窗口和VIP窗口没有获取到任务就去服务普通客户
commonService();
}
}
//为VIP客户服务
privatevoid vipService() {
String windowName ="第"+windowId+"号"+type+"窗口";
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;
long serveTime =new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serveTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
long costTime = System.currentTimeMillis()-beginTime;
System.out.println(windowName+"为第"+number+"个"+type+"客户完成服务,耗时"+costTime/1000+"秒");
}else{
System.out.println(windowName+"没有取到任务!");
commonService();
}
}
}
7. 编写程序的主类和完成客户取号功能
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
publicclass MainClass
{
publicstaticvoid 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.start();
//vip窗口
ServiceWindow vipWindow =new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
//vipWindow.setWindowId(6);
vipWindow.start();
//模拟客户来服务,往数组中添加号码
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
publicvoid 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(){
publicvoid run()
{
Integer number = NumberMachine.getInstance().getVipManager().generateNewManager();
System.out.println(number+"号VIP客户等待服务!");
}},
0,
//vip:普通客户为1:6
Constants.COMMON_CUSTOMER_INTERVAL_TIME*6,
TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
publicvoid run()
{
Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager();
System.out.println(number+"号快速客户等待服务!");
}},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME*2,
TimeUnit.SECONDS);
}
}
8. 总成测试和修正Bug
测试结果如下:(分析)
第1号普通窗口正在获取任务
第1号普通窗口没有取到任务,先休息1秒钟嘛
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务!
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务,先休息1秒钟嘛
第2号普通窗口正在获取任务
第2号普通窗口没有取到任务,先休息1秒钟嘛
第4号普通窗口正在获取任务
第4号普通窗口正在为第1个普通客户服务
号普通客户等待服务!
第1号VIP窗口正在获取任务
号快速客户等待服务!
第1号VIP窗口没有取到任务!
第1号VIP窗口正在获取任务
第1号VIP窗口没有取到任务,先休息1秒钟嘛
号VIP客户等待服务!
第1号普通窗口正在获取任务
第1号普通窗口没有取到任务,先休息1秒钟嘛
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口正在获取任务
第1号快速窗口正在为第1个快速客户服务
号普通客户等待服务!
第2号普通窗口正在获取任务
第2号普通窗口正在为第2个普通客户服务
第1号VIP窗口正在获取任务
第1号VIP窗口正在为第1个VIP客户服务
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号普通窗口正在获取任务
第1号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口为第1个快速客户完成服务,耗时1秒
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务!
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务,先休息1秒钟嘛
号普通客户等待服务!
号快速客户等待服务!
第1号VIP窗口为第1个VIP客户完成服务,耗时1秒
第1号VIP窗口正在获取任务
第1号VIP窗口没有取到任务!
第1号VIP窗口正在获取任务
第1号VIP窗口正在为第3个普通客户服务
第4号普通窗口为第1个普通客户完成服务,耗时2秒
第4号普通窗口正在获取任务
第4号普通窗口没有取到任务,先休息1秒钟嘛
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号普通窗口正在获取任务
第1号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口正在获取任务
第1号快速窗口正在为第2个快速客户服务
号普通客户等待服务!
第4号普通窗口正在获取任务
第4号普通窗口正在为第4个普通客户服务
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号普通窗口正在获取任务
第1号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口为第2个快速客户完成服务,耗时1秒
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务!
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务,先休息1秒钟嘛
号普通客户等待服务!
号快速客户等待服务!
第1号普通窗口正在获取任务
第1号普通窗口正在为第5个普通客户服务
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口正在获取任务
第1号快速窗口正在为第3个快速客户服务
号普通客户等待服务!
第2号普通窗口为第2个普通客户完成服务,耗时4秒
第2号普通窗口正在获取任务
第2号普通窗口正在为第6个普通客户服务
第1号VIP窗口为第3个普通客户完成服务,耗时2秒
第1号VIP窗口正在获取任务
第1号VIP窗口没有取到任务!
第1号VIP窗口正在获取任务
第1号VIP窗口没有取到任务,先休息1秒钟嘛
第3号普通窗口正在获取任务
第3号普通窗口没有取到任务,先休息1秒钟嘛
第1号快速窗口为第3个快速客户完成服务,耗时1秒
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务!
第1号快速窗口正在获取任务
第1号快速窗口没有取到任务,先休息1秒钟嘛
思考:1、在写代码过程中先要搭好框架,然后发现那里需要什么就去写什么;比如要用的很多常量,就写一个常量类
/**
*
常量类:用来表示为客户服务的最长事件和最小时间
*
还有普通客户每隔多少时间来一个
*/
publicclass Constants
{
publicstaticintMAX_SERVICE_TIME
= 10000;
publicstaticintMIN_SERVICE_TIME
= 1000;
publicstaticintCOMMON_CUSTOMER_INTERVAL_TIME=1;
}
2、当快速和vip窗口没任务时会去帮普通客户服务,它只是调用为普通客户服务的方法,单窗口还是自己对应的窗口,所以在commonService()方法中windowName还是自己
的名字,但下面为什么客户服务要写死,写成普通,而不能用窗口的属性值type代替。这样才能看到 第几号vip(快速)窗口正在为第几号普通客户服务
3、整个系统中主要类只有三个,一个是窗口类ServiceWindow,一个是号码管理器类NumberManager,一个是号码机器类NumberMachine。