------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
银行业务调度系统
银行业务调度系统也是一个非常重要的实训项目。
业务系统需求分析:
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方式展现程序运行结果。
面向对象的分析与设计:
有三种对应类型的客户:VIP客户,普通客户,快速客户 ,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务。
●首先,经常在银行办理业务的人更有利于理解本系统,例如,我经常陪老婆跑银行,对银行的这个业务算是比较熟悉了,我知道每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,我想到要有一个号码管理器对象,让这个对象不断地产生号码,就等于随机生成了客户。
由于有三类客户,每类客户的号码编排都是完全独立的,所以,我想到本系统一共要产生三个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。
各类型客户在其对应窗口按顺序依次办理业务 ,准确地说,应该是窗口依次叫号。
●各个窗口怎么知道该叫哪一个号了呢?它一定是问的相应的号码管理器,即服务窗口每次找号码管理器获取当前要被服务的号码。
●如果我不是多次亲身经历银行的这种业务,再加之积累了大量面向对象的应用开发经验,我也不知道能否轻松进行这种设计,能否发掘出其中隐含的对象信息,我真说不出具体的经验是什么,就是日积月累出来的一种感觉。难道这就是传说中的:“只可意会,不可言传?”
自己总结:取号的机制可以是一个取号机器,由它来设置取号系统。而客户有三种,由要求可知,每个客户的叫号是独立的,所以要有三个号码机制,又因为3个号码由一个机器取号,所以运用单例设计,只有一个对象。当各个类型的客户叫号的时候,各个窗口向取号机器寻找相应类型号码,这样才能响应客户的需求。
经分析,本题除了主函数类之外主要的类还有以下几个:
1.号码管理器类
定义集合存储号码,号码就相当于顾客。同时内部应存在增加和删除号码的方法,来了顾客增加号码,服务完毕删除号码。
2.号码机器类
由于有三类客户,普通,快速和vip,每类客户的号码编排都是完全独立的,需要建立三个不同的号码管理器类,各自管理自己的用户排队号码。这三个号码管理器对象统一由一个号码机器进行管理,所以,他需要设计成单利模式。
3.服务窗口类
各类型客户在其对应窗口按顺序依次办理业务,窗口依次叫号,即窗口每次需要利用号码管理器获取当前要被服务的号码,叫号成功即删除该号码。定义三类窗口的服务方法,普通窗口没有客户时等待1秒再次获取客户,快速和vip窗口没有顾客就获取普通顾客,调用普通窗口的服务方法。
代码实现分析:
具体到程序可以有3个类,NumberManager,NumberMachine,ServiceWindow。真正操作成还是NumberManager,因为掌握数据的可以控制运行。
NumberMachine,除了3个类型用户,还有一个方法可以调用自身对象getInstance()。
1、 NumberManager类编写:
设置机器出号以及服务号码
号码管理器类
package bank;
import java.util.ArrayList;
import java.util.List;
/*
* 建立号码管理器类,建立集合管理号码元素,管理器有两个功能,客户来了产生新的号码,窗口叫号时将要被服务的号码返回给窗口,
* 并从集合中出去已被服务的客户号码
*/
public class NumberManager {
private int lastNumber = 0;
private List<Integer> queue = new ArrayList<Integer>();// 定义集合记录客户号码,便于增删
// 定义增加客户资料的方法,增加和取出方法操作同一个数据,需要同步
public synchronized Integer creatNumber() {
queue.add(++lastNumber);// 添加对象到客户集合
return lastNumber;
}
// 定义叫号方法,即从集合中取出对象
public synchronized Integer fachNumber() {
if (queue.size() > 0) {
return (Integer) queue.remove(0);// 当集合不为空时,窗口叫号会从等待客户号码最靠前的一个开始叫,获得服务资格之后,删除号码
} else {
return null;
}
}
}
2、NumberMachine的编写:
这个类才用枚举,并且私有化,对外提供一个方法,调用三个枚举对象。
号码机器类
package bank;
/*
* 建立银行的排号机器类,所有客户都操作同一台机器,属于单例模式,排号机器类中有三个对象,即普通,快速,vip对象
* 每个对象都具有各自的客户数量属性,即有具有各自的号码管理器
*/
public class NumberMachine {
// 建立单利设计模式
private NumberMachine() {
};
private final static NumberMachine instance = new NumberMachine();
public static NumberMachine getInstance() {
return instance;
}
// 创建三个客户类型所对应的号码管理器类
private NumberManager commonManager = new NumberManager();
private NumberManager quickManager = new NumberManager();
private NumberManager vipManager = new NumberManager();
// 建立获取三种号码管理器对象的方法
public NumberManager getCommonManager() {
return commonManager;
}
public NumberManager getQuickManager() {
return quickManager;
}
public NumberManager getVipManager() {
return vipManager;
}
}
3、ServiceWindow类编写:
(重点)服务窗口为3个,为多线程独立运行,普通窗口服务时间随机为1-10秒,其余2个窗口都是1秒,如果没有快速和VIP客户就来接待普通客户。
服务窗口类
package bank;
import java.util.Random;
import java.util.concurrent.Executors;
/*
* 定义窗口服务类,窗口类有服务方法,每个各户类型具有不同的服务方法,故需要定义三个不同的方法,
* 普通窗口的服务流程为:
* 获取任务--获取成功则开始处理任务(获取客户号码并从号码管理器中移除该客户)--一段时间后任务处理完成--获取下个任务
* 获取任务--没有客户,获取失败--等待一定时间,重新获取
* 快速和vip窗口情况类似:
* 获取任务--获取成功则开始处理任务(获取客户号码并从号码管理器中移除该客户)--一段时间后任务处理完成--获取下个任务
* 获取任务--没有对应客户,获取失败--获取普通客户任务
*/
public class ServiceWindow {
// 定义窗口的类型(即为客户类型)和号码
private CustomerType type = CustomerType.COMMON;// 初始化类型为普通客户类
private int number = 1;// 初始化窗口号码为1号窗口
// 获取窗口类型
public CustomerType getType() {
return type;
}
public void setType(CustomerType type) {
this.type = type;
}
// 获取窗口号码
public int getNumber() {
return number;
}
// 传入号码参数
public void setNumber(int number) {
this.number = number;
}
// 定义窗口的启动方法,根据窗口类型的不同,调用对应类型窗口的服务方法
public void start() {
Executors.newSingleThreadExecutor().execute(new Runnable() {
public void run() {
while (true) {
switch (type) {
case COMMON:
commonService();
break;
case QUICK:
quickService();
break;
case VIP:
vipService();
break;
}
}
}
});
}
// 定义普通窗口的服务方法
private void commonService() {
System.out.println("第" + number + "号" + type + "窗口开始获取任务");
// 调用普通客户号码管理类的叫号方法获取要服务客户的号码
Integer serviceNumber = NumberMachine.getInstance().getCommonManager()
.fachNumber();
if (serviceNumber != null) {
System.out.println("第" + number + "号" + type + "窗口开始为第"
+ serviceNumber + "号普通客户服务");
// 随机产生一段服务时间
int serviceTime = new Random().nextInt(Time.MAX_SERVICE_TIME
- Time.MIN_SERVICE_TIME)
+ Time.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + number + "号" + type + "窗口完成为第"
+ serviceNumber + "号普通客户的服务,用时" + serviceTime / 1000 + "秒");
} else {
// 当没有客户等待时,窗口休息一秒钟
System.out.println("第" + number + "号" + type + "窗口没有取到普通任务,空闲一秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 定义快速窗口服务方法
private void quickService() {
System.out.println("第" + number + "号" + type + "窗口开始获取任务");
// 调用快速客户号码管理类的叫号方法获取要服务客户的号码
Integer serviceNumber = NumberMachine.getInstance().getQuickManager()
.fachNumber();
if (serviceNumber != null) {
System.out.println("第" + number + "号" + type + "窗口开始为第"
+ serviceNumber + "号快速客户服务");
// 快速客户的服务时间是最小服务时间
int serviceTime = Time.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + number + "号" + type + "窗口完成为第"
+ serviceNumber + "号快速客户的服务,用时" + serviceTime / 1000 + "秒");
} else {
// 当没有快速客户等待时,快速窗口为普通客户服务
System.out.println("第" + number + "号" + type + "窗口没有取到快速任务");
commonService();
}
}
// 定义vip窗口服务方法
private void vipService() {
System.out.println("第" + number + "号" + type + "窗口开始获取任务");
// 调用vip客户号码管理类的叫号方法获取要服务客户的号码
Integer serviceNumber = NumberMachine.getInstance().getVipManager()
.fachNumber();
if (serviceNumber != null) {
System.out.println("第" + number + "号" + type + "窗口开始为第"
+ serviceNumber + "号vip客户服务");
// 随机产生一段服务时间
int serviceTime = new Random().nextInt(Time.MAX_SERVICE_TIME
- Time.MIN_SERVICE_TIME)
+ Time.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out
.println("第" + number + "号" + type + "窗口完成为第"
+ serviceNumber + "号vip客户的服务,用时" + serviceTime
/ 1000 + "秒");
} else {
// 当没有vip客户等待时,vip窗口为普通客户服务
System.out.println("第" + number + "号" + type + "窗口没有取到vip任务");
commonService();
}
}
}
4 CustomerType枚举类编写:定义枚举类型
客户类型类
package bank;
//建立客户类型类,分为三种类型,是一个枚举类
public enum CustomerType {
COMMON, QUICK, VIP;
// 定义toString方法,返回值是String类型,使程序输出对应的中文客户类型
public String toString() {
String name = null;
switch (this) {
case COMMON:
name = "普通";
break;
case QUICK:
name = "快速";
break;
case VIP:
name = name();
break;
}
return name;
}
}
5 Time类
时间类
package bank;
//建立时间类,定义生成客户时间间隔以及业务办理时间最大值和最小值,自成一类便于修改设置
public class Time {
// 三个时间是被几个类所共享的数据,设置成静态数据
// 定义最大服务时间
public static int MAX_SERVICE_TIME = 10000;
// 定义最小服务时间
public static int MIN_SERVICE_TIME = 1000;
// 定义产生普通客户的时间间隔,按比例可以得到快速和vip客户产生间隔
public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}
6 MainClass类编写:
主函数首先创建4个独立线程的窗口,然后建立用户机制,让普通用户快速用户和VIP用户按照一定比较来办理业务。
主函数类
package bank;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/*
* 建立主类,创建六个窗口对象并运行,窗口对象具有类型和号码属性,同时调用号码管理器对象生成客户
*/
public class MainClass {
public static void main(String[] args) {
// 创建4个普通窗口对象并运行
for (int x = 1; x < 5; x++) {
ServiceWindow commonWindow = new ServiceWindow();
commonWindow.setNumber(x);
commonWindow.start();
}
// 创建一个快速窗口并运行
ServiceWindow quickWindow = new ServiceWindow();
quickWindow.setType(CustomerType.QUICK);
quickWindow.start();
// 创建一个vip窗口并运行
ServiceWindow vipWindow = new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
vipWindow.start();
// 普通客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getCommonManager().creatNumber();
System.out.println("第" + serviceNumber + "号普通客户正在等待服务");
}
}, 0, Time.COMMON_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
// 快速客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getQuickManager().creatNumber();
System.out.println("第" + serviceNumber + "号快速客户正在等待服务");
}
}, 0, Time.COMMON_CUSTOMER_INTERVAL_TIME * 2, TimeUnit.SECONDS);
// vip客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getVipManager().creatNumber();
System.out.println("第" + serviceNumber + "号vip客户正在等待服务");
}
}, 0, Time.COMMON_CUSTOMER_INTERVAL_TIME * 6, TimeUnit.SECONDS);
}
}
总结:
1. 如果在程序中某个对象要求只有一个需要用到单例设计模式
例如:二、NumberMachine类设计成单例。
2.如果系统中出现具有固定类型的数据最好使用枚举
例如:三、CustomerType枚举类本题三种类型的客户
3. 对于有固定频率实现的数据使用定时器的应用
例如:五、MainClass类
4. 在开发中通常专门写一个类 定义常量方便后期的数据更新
例如:六、Constants类