(2)面向对象设计
1) 队号机对应抽象为NumberMachine类
需要定义一个成员变量来表示排队号,每次产生对应一个客户排队号码变量自增一次
需要定义一个用于存储所有等待服务的客户号码的队列集合,集合的添加删除元素就是
产生客户号码和删除已经服务完毕的客户号码的过程。
需要定义两个功能一个是产生新号码的方法内部给集合添加元素,元素就是排队号
一个获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,
所以,要进行同步。
代码:
package com.itheima.bankqueque;
import java.util.ArrayList;
import java.util.List;
/**
*
* 排队号机
* @author Administrator
* */
public class NumberMachine {
private int lastNumber = 0;//排队编号
private List queueNumbers = new ArrayList();//用集合来存放等待到服务结束的号码
/*产生排队号码,对应客户通过对应排队号码机的该方法来取得排队号*/
public synchronized Integer generateNewNumber(){
queueNumbers.add(++lastNumber);
return lastNumber;
}
/*服务窗口服务完毕对应排队号的客户时调用该方法,表示持该号码的客户已服务结束*/
public synchronized Integer fetchNumber(){
if(queueNumbers.size()>0){
return (Integer)queueNumbers.remove(0);
}else{
return null;
}
}
}
2) 号码机的调度器抽象为 NumberMachineManager类
定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户
的号码机,定义三个对应的方法来返回这三个NumberManager对象。
将NumberMachine类设计成单例。
代码:
package com.itheima.bankqueque;
/**
*
* 协调管理三种客户排队号码机的管理器
**/
public class NumberMachineManager {
/*将排队号码产生器设计为单例*/
private static NumberMachineManager instance = new NumberMachineManager();
/*产生三个排队号码管理器,分别管理三种客户的排队号*/
private NumberMachine commonManager = new NumberMachine();
private NumberMachine expressManager = new NumberMachine();
private NumberMachine vipManager = new NumberMachine();
private NumberMachineManager(){}
/*获取单例对象*/
public static NumberMachineManager getInstance(){
return instance;
}
/*获取普通客户排队号管理器*/
public NumberMachine getCommonManager() {
return commonManager;
}
/*获取快速客户排队号管理器*/
public NumberMachine getExpressManager() {
return expressManager;
}
/*获取VIP客户排队号管理器*/
public NumberMachine getVipManager() {
return vipManager;
}
}
3) 客户类型抽象为 CustomerType枚举类
系统中有三种类型的客户,所以用定义为枚举类,每个元素对应表示一种客户类型。
重写toString方法,返回对应客户类型的中文名称。
代码:
package com.itheima.bankqueque;
/*定义客户类型*/
public enum CustomerType {
COMMON,EXPRESS,VIP;
/**
* 返回当前客户类型对应的名称
* */
@Override
public String toString(){
String name = null;
switch(this){
case COMMON:
name = "普通";
break;
case EXPRESS:
name = "快速";
break;
case VIP:
name = name();
break;
}
return name;
}
}
4) 服务窗口抽象为 ServiceWindow类
定义三个方法分别对应于为三种客户进行服务.
定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个对应服务方法。
定义三个服务三种客户类型的方法,具体实现服务窗口服务三种客户的功能,包括叫号功能,所有类型
的窗口在都先通过调度器获取到对应的号码机,并通过号码机获取对应的客户
号码,如果对应号码机中存在号码则获取叫号,如果没有,那么对应普通窗口会等待1秒再继续尝试获取
对于VIP和快速窗口从对应号码机中获取不到号码时会继续从普通号码机中去尝试获取,如果获取到
则为普通客户服务,获取不到则休息1秒 后继续尝试从对应的号码机中获取客户号码,依次循环
通过线程的随机时间间隔等待来模拟为一个客户服务
代码:
package com.itheima.bankqueque;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
*
*服务窗口
**/
public class ServiceWindow {
private static Logger logger = Logger.getLogger("cn.itcast.bankqueue");
private CustomerType type = CustomerType.COMMON;//初始化为普通客户
private int number = 1;//窗口编号,初始化为1号窗口
//返回当前服务客户类型
public CustomerType getType(){
return type;
}
//设置当前服务客户类型
public void setType(CustomerType type) {
this.type = type;
}
//设置当前服务窗口号
public void setNumber(int number){
this.number = number;
}
/**
*
* 启动窗口服务
* */
public void start(){
/*
* 采用JDK1.5的新的创建线程方法创建并启动线程
* 通过匿名内部类直接实现Runnable
* */
Executors.newSingleThreadExecutor().execute(
new Runnable(){
public void run(){
/*通过循环不断检查当前的type并执行各个服务窗口的对应服务*/
while(true){
switch(type){
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}
}
}
);
}
/*普通窗口的服务方法*/
private void commonService(){
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取普通任务!");
//普通窗口在调用调度器的获取普通号码机,并通过普通号码机尝试获取存在的客户号码
Integer serviceNumber = NumberMachineManager.getInstance().getCommonManager().fetchNumber();
//假如获取到号码,则为之服务
if(serviceNumber != null ){
System.out.println(windowName + "开始为第" + serviceNumber + "号普通客户服务");
//最大时间时间
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
//获取服务时间,1~10秒
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
//模拟正在为客户服务
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号普通客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//如果没有获取到号码则休息1秒
System.out.println(windowName + "没有取到普通任务,正在空闲一秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void expressService(){
//快速窗口通过调度器获取其中的快速客户号码机的号码
Integer serviceNumber = NumberMachineManager.getInstance().getExpressManager().fetchNumber();
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取快速任务!");
//假如获取到号码,则为之服务
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号快速客户服务");
//为快速客户服务的时间为1秒
int serviceTime = Constants.MIN_SERVICE_TIME;
//模拟正在为快速客户服务
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号快速客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//如果快速窗口没有获取到快速客户号码则启动普通服务方法,执行普通窗口一样的功能
System.out.println(windowName + "没有取到快速任务!");
commonService();
}
}
/**
* VIP服务窗口
*/
private void vipService(){
//通过号码机调度器获取VIP号码机的客户号码
Integer serviceNumber = NumberMachineManager.getInstance().getVipManager().fetchNumber();
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取VIP任务!");
//假如获取到VIP客户号码则服务之
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号VIP客户服务");
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
try {//模拟正在为VIP客户服务
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{//假如没获取到VIP客户号码,则调用普通窗口方法
System.out.println(windowName + "没有取到VIP任务!");
commonService();
}
}
}
package com.itheima.bankqueque;
public class Constants
{
public static int MAX_SERVICE_TIME = 10000; //10秒!
public static int MIN_SERVICE_TIME = 1000; //1秒!
/* 为一个普通客户服务平均最快1秒钟完毕 ,假设间隔1秒钟产生一个普通客户*/
public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}
package com.itheima.bankqueque;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class MainClass {
private static Logger logger = Logger.getLogger("cn.itcast.bankqueue");
public static void main(String[] args) {
//产生4个普通窗口
for(int i=1;i<5;i++){
ServiceWindow window = new ServiceWindow();
window.setNumber(i);
window.start();
}
//产生1个快速窗口
ServiceWindow expressWindow = new ServiceWindow();
expressWindow.setType(CustomerType.EXPRESS);
expressWindow.start();
//产生1个VIP窗口
ServiceWindow vipWindow = new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
vipWindow.start();
//普通客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//普通客户通过普通客户对应的号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getCommonManager().generateNewNumber();
/*
* 采用logger方式,无法看到直观的运行效果,因为logger.log方法内部并不是直接把内容打印出出来,
* 而是交给内部的一个线程去处理,所以,打印出来的结果在时间顺序上看起来很混乱。
**/
//logger.info("第" + serviceNumber + "号普通客户正在等待服务!");
System.out.println("第" + serviceNumber + "号普通客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME,
TimeUnit.SECONDS);
//快速客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//快速客户通过快速客户号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getExpressManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号快速客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2,
TimeUnit.SECONDS);
//VIP客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
//VIP客户通过VIP客户号码机获取号码
Integer serviceNumber = NumberMachineManager.getInstance().getVipManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号VIP客户正在等待服务!");
}
},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6,
TimeUnit.SECONDS);
}
}