一、需求分析
1、银行有三种服务窗口:4个普通窗口,1个VIP窗口,1个快速窗口。
2、普通窗口只为普通客户服务。
3、VIP窗口优先为VIP客户服务,如果没有VIP客户就为普通客户服务。快速窗口为快速客户(办理交水电费等)服务,如果没有快速客户就为普通客户服务。
4、异步随机生成客户,且普通客户,VIP客户,快速客户的比例是:6:1:3。
5、服务顺序按照号码排序,不同用户按照不同服务类型取不同的号码,每种号码都按先后顺序排队,先来先享受服务。
6、窗口按照顺序叫号。
二、面向对象的分析与设计
1、对象:
(1)号码管理器,三种不同的客户,三种不同的号码表示。
(2)号码产生器,一个号码产生器能产生不同的号码,单例设计模式。
(3)服务窗口,能够生成不同服务窗口。
2、设计图:
3、号码管理器NumberManager:
能够生成客户号码,每来一个客户,就生成相应类型的号码,并把号码按顺序添加到集合中,generateNewNumber()方法。服务窗口能获取当前正在排队的号码,并且叫号之后,将该号码删除,fetchNumber()方法,如果没有客户则返回null。
4、号码产生器NumberMachine:
相当于银行里那个产生号码的机器。只有一台,所以使用单例设计模式。号码产生器能根据需求产生三种不同的号码。所以需要有三个NumberManager的对象,分别产生不同的客户类型号码。
5、服务窗口ServiceWindow:
首先要设置自己的窗口类型type。如果是普通窗口,就叫普通号,有客户就服务,没有客户就休息一会再叫普通号。如果是VIP窗口,就叫VIP客户,有就服务,没有VIP客户就叫普通客户的号。快速窗口与VIP窗口同理,先叫快速客户,有就为快速客户服务,没有快速客户就叫普通客户的号,为普通客户服务。服务的过程通过相应NumberManager实例的fetchNumber()方法获取当前正在等待的客户号。可以使用Thread.sleep()方法模拟服务时间。使用while循环使服务窗口不停的执行命令,服务客户。
四、代码实现:
1、号码管理器NumberManager:
import java.util.ArrayList;
import java.util.List;
public class NumberManager {
private int lastNumber = 0;
private List<Integer> numberQuene = new ArrayList<Integer>();//面向接口编程
//生成号码,并添加到队列中等待
//返回值最好写成Integer,以防null的出现
public synchronized Integer generateNewNumber(){
numberQuene.add(++lastNumber);
return lastNumber;
}
public synchronized Integer fetchNumber(){
//如果一个客户都没有,集合为空,则返回null,所以函数类型应使用Integer而不是int
if(numberQuene.size()>0){
return numberQuene.remove(0);
}else{
return null;
}
}
}
2、号码产生器NumberMachine:
/**
* 能生成三种号码,所以要有三个号码管理器NumberManager对象
* */
public class NumberMachine {
private NumberManager commonManager = new NumberManager();
private NumberManager VIPManager = new NumberManager();
private NumberManager ExpressManager = new NumberManager();
public NumberManager getCommonManager() {
return commonManager;
}
public NumberManager getVIPManager() {
return VIPManager;
}
public NumberManager getExpressManager() {
return ExpressManager;
}
//单例设计模式,饿汉式
private static NumberMachine instance = new NumberMachine();
private NumberMachine(){}
public static NumberMachine getInstance(){
return instance;
}
}
3、服务窗口ServiceWindow:
import java.util.Random;
import java.util.concurrent.Executors;
public class ServiceWindow {
//这里需要一个默认的值吗,不一定,可以设置默认为普通,也可在生成窗口时再设置
private CustomerType type;
//number 为了方便表示第几个普通窗口,默认设置为1,这样VIP和快速窗口就不用设置了
private int number = 1;
//便于生成服务窗口时设置窗口类型
public void setType(CustomerType type) {
this.type = type;
}
//便于生成普通服务窗口时生成窗口号
public void setNumber(int number) {
this.number = number;
}
public void start(){
//使用线程池
Executors.newSingleThreadExecutor().execute(new Runnable(){
@Override
public void run() {
//while无限循环,使其不停得执行命令,其实更高效做法是将while放在case下面,为了好看才如此
while(true){//switch比if else的效率为什么高,switch的类型可以有哪些?枚举反正是可以的
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 = NumberMachine.getInstance().getCommonManager().fetchNumber();
if(serviceNumber != null){
System.out.println(windowName+"正在为"+serviceNumber+"号普通客户服务");
//设置普通客户服务时间为一个范围,且服务时间在该范围内随机
int maxRadom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRadom)+Constants.MIN_SERVICE_TIME;
try {
//用线程休眠时间代表服务时间
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(windowName+"为"+serviceNumber+"号普通客户服务完毕!");
}else{
System.out.println(windowName+"没有取到普通任务,休息1秒钟");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void ExpressService() {
String windowName = number+"号"+type+"窗口";
System.out.println(windowName+"正在获取快速--任务");
Integer serviceNumber = NumberMachine.getInstance().getExpressManager().fetchNumber();
if(serviceNumber != null){
System.out.println(windowName+"正在为"+serviceNumber+"号快速客户服务");
try {
Thread.sleep(Constants.MIN_SERVICE_TIME);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(windowName+"为"+serviceNumber+"号快速客户服务完毕!");
}else{
System.out.println(windowName+"没有取到快速任务,去获取普通客户任务");
commonService();
}
}
private void VIPService() {
String windowName = number+"号"+type+"窗口";
System.out.println(windowName+"正在获取VIP任务");
Integer serviceNumber = NumberMachine.getInstance().getVIPManager().fetchNumber();
if(serviceNumber != null){
System.out.println(windowName+"正在为"+serviceNumber+"号VIP客户服务");
int maxRadom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRadom)+Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(windowName+"为"+serviceNumber+"号VIP客户服务完毕!");
}else{
System.out.println(windowName+"没有取到VIP任务,去获取普通客户任务");
commonService();
}
}
}
4、测试类BankTest:
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class BankTest {
/**
* @param args
*/
public static void main(String[] args) {
//产生四个普通窗口
for(int i = 1;i<5;i++){
ServiceWindow commonWindow = new ServiceWindow();
commonWindow.setNumber(i);
commonWindow.setType(CustomerType.COMMON);
commonWindow.start();
}
//生成一个VIP窗口
ServiceWindow VIPWindow = new ServiceWindow();
VIPWindow.setType(CustomerType.VIP);
VIPWindow.start();
//生成一个快速窗口
ServiceWindow ExpressWindow = new ServiceWindow();
ExpressWindow.setType(CustomerType.EXPRESS);
ExpressWindow.start();
//普通客户拿号码
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
Integer commonServiceNumber = NumberMachine.getInstance().getCommonManager().generateNewNumber();
System.out.println("第个"+commonServiceNumber+"普通客户正在等待服务");
}},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME,
TimeUnit.SECONDS);
//VIP客户取号码
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
Integer VIPServiceNumber = NumberMachine.getInstance().getVIPManager().generateNewNumber();
System.out.println("第个"+VIPServiceNumber+"VIP客户正在等待服务");
}},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME*6,
TimeUnit.SECONDS);
//快速客户取号码
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
Integer ExpressServiceNumber = NumberMachine.getInstance().getExpressManager().generateNewNumber();
System.out.println("第个"+ExpressServiceNumber+"快速客户正在等待服务");
}},
0,
Constants.COMMON_CUSTOMER_INTERVAL_TIME*2,
TimeUnit.SECONDS);
}
}
5、提取的静态变量:
public class Constants {
public static int MAX_SERVICE_TIME = 10000;//10秒,因为sleep单位是ms
public static int MIN_SERVICE_TIME = 1000;//1秒,因为sleep单位是ms
public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;//用于每一秒产生一个普通客户
}
6、客户类型枚举:
public enum CustomerType {
COMMON,VIP,EXPRESS;
//复写toString方法,当打印类型名时,COMMON打印出“普通”等,更加友好
public String toString(){
String name = null;
switch(this){
case COMMON:
name = "普通";
break;
case VIP:
name = "VIP";
break;
case EXPRESS:
name = "快速";
break;
}
return name;
}
}
五、小知识点总结:
1、if …else 为什么比switch…case效率低
这是因为if…else每次都要从头遍历查询,直到命中。而switch…case是生成跳转表,根据跳转表,直接跳转到符合条件的语句,减少了遍历次数,所以效率高。
2、面试题:switch(option)…case中option的类型可以有哪些?
可以是int、short、byte、char和枚举类型的。