您的位置:首页 > 职场人生

黑马程序员:感悟“银行业务调度系统”

2012-05-11 09:27 281 查看
---------------------- android培训java培训、期待与您交流! -----------------------

需求分析:

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

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

*异步随机生成各种类型的客户,生成各类型客户概率比例:VIP客户:普通客户:快速客户=1:6:3

*客户办理业务所需时间有最大、最小值,在该范围内随机设定每个VIP客户及普通客户办理业务所需要的时间, 快速客户办理业务所需时间为最小值(办理业务的过 程可通过sleep())模拟

*各类型客户在其对应窗口依次办理业务(由窗口叫号)

*当VIP(6号)窗口和快速窗口没有客户等待办理业务时,可以办理普通客户业务。而一旦有普通客户等待办理业务, 优先处理对应客户的业务

*随机生成客户时间间隔及业务办理时间最大值、最小值

*不必实现GUI,只实现业务逻辑,可通过LOG方式展现程序运行结果

面向对象的分析设计

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

每个客户的表示方法:取号机产生一个号码。所以要有号码管理器对象(不断产生随机号码)

三类客户的号码编排是完全独立的,所以要有3个号码管理器对象,各自管理一类客户的排除号码

这3个号码管理器对象统一由一个号码机器管理(在号码机器选择要办理哪种客户类型的业务),这个号码机器在系统中始终只能有一个,设计成单例

*各类型客户在其对应窗口依次办理业务(窗口依次叫号)

窗口怎么知道叫哪个号码?它应该询问号码管理器。即:服务窗口每次找号码管理器获取当前要被服务的号码

画类图,使思路清晰:



2. 类的实现

*NumberManager类:

成员变量:存储上一个客户号码

等待服务的客户号码的队列集合

方法:生成新号码 generateNewNum()

得到马上要为之服务的号码 fetchServiceNum() 注意:预防空指针异常,返回值设置为Integer
难点:由于方法 generateNewNum 与方法 CallServiceNum 访问相同的资源 numsQueue,所以考虑同步
public synchronized Integer generateNewNum() {
numsQueue.add(lastNum);
return lastNum++;
}

public synchronized Integer CallServiceNum() {
return numsQueue.remove(0);
}


*NumberMachine类

先构建普通对象,生成管理三种类型客户的方法:getCommonManager()、getVIPManager()、getQuickManager()

单例化

*ServiceWindow类

NumberMachine内部叫号的过程外部没必要了解,只需要对外提供一个开始工作的方法(start()),之后机器就

不停叫号!
注意:枚举的toString()方法
public String toString() {
switch (this) { //this的使用
case COMMON:
return "普通";
case QUICK:
return "快速";
case VIP:
return name();
}
return null;
}

机器叫号时根据当前窗口的类型呼叫对应客户类型的号码。所以要有标记窗口类型的变量(用枚举最合适)
在判断条件非常多时,switch比if...else效率高
如何区别哪种类型的服务窗口或普通窗口的第几号窗口:要定义变量 windowID
可以把 windowID 和 CustomerType 作为构造方法参数来创建ServiceWindow对象,但不灵活、扩展性不好:不利于将来更改窗口号、更换服务窗口!所以用set(...)方法

服务时间随机生成在 Constant.MIN_SERVICE_TIME 与 Constant.MAX_SERIVCE_TIME 之间。注意将常量单独定义在类中的方法!
注意提取方法快捷操作:extract 的使用! commonService()的抽取使代码更有条理!但更好的做法是将VIP窗口、快速窗口作为普通窗口的子类,将逻辑与commonService()方法中不同的代码单独抽取为方法,并重写!可以最大限度地重用代码(因为quickService()与VIPService()方法的逻辑差不多)
private void commonService() {
String windowName = "第 " + windowID + " 号 " + ct + " 窗口" ;
System.out.println(windowName + " 正在叫号……");
Integer num = NumberMachine.getInstance().getCommonManager().fetchServiceNum();
if(null != num){
long startTime = System.currentTimeMillis();

int maxRand = Constant.MAX_SERIVCE_TIME - Constant.MIN_SERVICE_TIME;
long serviceTime = new Random().nextInt(maxRand) + 1 + Constant.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}

long costTime = System.currentTimeMillis() - startTime;
System.out.println(windowName + " 为第 "+ num +" 个 " + ct + " 客户服务耗时 "+ (costTime/1000) +" 秒!");
} else {
System.out.println(windowName + " 没有正在等待的客户!等待中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


测试类:

难点:异步随机生成各种类型的客户,生成各类型客户概率比例:VIP客户:普通客户:快速客户=1:6:3
通过定时器参数体现生成的三种类型客户的概率比
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
int num = NumberMachine.getInstance().getVIPManager().generateNewNum();
System.out.println("第 " + num + " 个VIP客户正在等待……");
}
},
1,
Constant.COMMONCUSTOMER_INTERVAL_TIME * 6,
TimeUnit.SECONDS
);

注意:fetchServiceNum() 是同步方法,要将打印语句放在这个方法后面,程序会流畅
只有当numsQueue中有号码时才取号码,否则返回null
VIP窗口和快速窗口为普通客户服务的情况:commonService方法中要写死“普通”,而 快速 和 VIP 窗口可以写 ct
两种单例模式的区别:在多线程程序中懒汉式单例必须加synchronized关键字(我自己写的时候就是用了这种“懒汉式”单例,因为程序中有多线程,导致生成了多个NumberMachine对象的实例)
多线程中锁中套锁容易死锁!
下面是程序的线程图解:



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

详细请查看:http://edu.csdn.net/heima
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: