今天在网上看到一道关于银行业务调度系统的题目,感觉有点意思,就动手写了写代码,记录一下。
题目:
模拟实现银行业务调度系统逻辑,具体需求如下:
*银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
*有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
*异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
*客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,
快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
*各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,
而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
对题目进行分析,
有三种类型的客户,他们的身份(职责)并不相同,但是他们的方法确实相同的,所以定义了三个方法相同的接口IVIPCustom,IExpressCustom和ICommonCustom。
package com.garychow.interfaces;
public interface IVIPCustom {
public int getNeedsTime();
public String getName();
}
package com.garychow.interfaces;
public interface IExpressCustom {
public int getNeedsTime();
public String getName();
}
package com.garychow.interfaces;
public interface ICommonCustom {
public int getNeedsTime();
public String getName();
}
三类客户分别对应了三种职责的窗口,普通窗口,快速窗口和VIP窗口。所以,也需要三个接口,ICommonServiceWindow,IExpressServiceWindow和IVIPServiceWindow。
package com.garychow.interfaces;
public interface ICommonServiceWindow {
public boolean service(ICommonCustom custom);
}
package com.garychow.interfaces;
public interface IExpressServiceWindow {
public boolean service(IExpressCustom custom);
}
package com.garychow.interfaces;
public interface IVIPServiceWindow {
public boolean service(IVIPCustom custom);
}
最后,有一个调度业务窗口和用户的系统,同时根据题意,是通过号码来进行管理调度的,我把他们的职责归纳到一起,所以有一个INumberMachine接口。
package com.garychow.interfaces;
// INumberMachine是整个系统的调度类,负责管理维护和处理服务顺序等
public interface INumberMachine {
// 产生一个普通客户
public void generateCommonCustom();
// 产生一个VIP客户
public void generateVIPCustom();
// 产生一个快速客户
public void generateExpressCustom();
// 为下一个普通客户服务
public void serviceNextCommonCustom();
// 为下一个VIP客户服务
public void serviceNextVIPCustom();
// 为下一个快速客户服务
public void serviceNextExpressCustom();
}
接口声明到此就结束了。
现在需要来考虑具体实现了:
首先需要定义常量:
package com.garychow.classes;
public class Constants {
public static final int GENERATE_INTERVAL_TIME = 1;
public static final int CUSTOM_NEEDS_TIME_MAX = 6;
public static final int CUSTOM_NEEDS_TIME_MIN = 2;
}
然后需要实现ICommonCustom,IExpressCustom和IVIPCustom接口。
package com.garychow.classes;
import com.garychow.interfaces.ICommonCustom;
public class CommonCustom implements ICommonCustom {
@Override
public int getNeedsTime() {
return time;
}
@Override
public String getName() {
return name;
}
private int time = (int)((Math.random() * (Constants.CUSTOM_NEEDS_TIME_MAX - Constants.CUSTOM_NEEDS_TIME_MIN)
+ Constants.CUSTOM_NEEDS_TIME_MIN) * 1000);
private String name = ++number + "号普通客户";
private static int number = 0;
}
package com.garychow.classes;
import com.garychow.interfaces.IVIPCustom;
public class VIPCustom implements IVIPCustom {
@Override
public int getNeedsTime() {
return time;
}
@Override
public String getName() {
return name;
}
private int time = (int)((Math.random() * (Constants.CUSTOM_NEEDS_TIME_MAX - Constants.CUSTOM_NEEDS_TIME_MIN)
+ Constants.CUSTOM_NEEDS_TIME_MIN) * 1000);
private String name = ++number + "号VIP客户";
private static int number = 0;
}
package com.garychow.classes;
import com.garychow.interfaces.IExpressCustom;
public class ExpressCustom implements IExpressCustom {
@Override
public int getNeedsTime() {
return time;
}
@Override
public String getName() {
return name;
}
private int time = (Constants.CUSTOM_NEEDS_TIME_MIN * 1000);
private String name = ++number + "号快速顾客";
private static int number = 0;
}
普通客户可以由任意一个窗口服务,而VIP客户和快速客户只能通过相应的窗口获得服务,因为,我认为这个可以设计为一个责任链,把普通客户交给链头,直到有窗口能服务客户或者都暂时不能服务为止。因为设计一个抽象类AResponsibilityChainCommonWindow实现ICommonServiceWindow接口。
package com.garychow.classes;
import com.garychow.interfaces.ICommonCustom;
import com.garychow.interfaces.ICommonServiceWindow;
public abstract class AResponsibilityChainCommonWindow implements ICommonServiceWindow {
private ICommonServiceWindow nextCommonServiceWindow;
public ICommonServiceWindow getNextCommonServiceWindow() {
return nextCommonServiceWindow;
}
public void setNextCommonServiceWindow(ICommonServiceWindow nextCommonServiceWindow) {
this.nextCommonServiceWindow = nextCommonServiceWindow;
}
@Override
public boolean service(ICommonCustom custom) {
if (null != nextCommonServiceWindow) {
return nextCommonServiceWindow.service(custom);
}
return false;
}
}
责任链已经搞定了,现在发现调度系统需要了解各个窗口的状态,好分配客户到窗口去。这是一个典型的观察者,而窗口们就是被观察者。因为实现一个观察者类WindowObserver,由它去通知INumberMachine。
package com.garychow.classes;
import com.garychow.interfaces.INumberMachine;
public class WindowObserver {
public static WindowObserver getInstance() {
return WindowObserverHolder.wo;
}
private static class WindowObserverHolder {
public static final WindowObserver wo = new WindowObserver();
}
private WindowObserver() {
}
public void commonWindowServiceFinish() {
nm.serviceNextCommonCustom();
}
public void vipWindowServiceFinish() {
nm.serviceNextVIPCustom();
}
public void expressWindowServiceFinish() {
nm.serviceNextExpressCustom();
}
private INumberMachine nm = NumberMachine.getInstance();
}
到这里整体的思路全部都清晰了,下面只要把方法都实现出来就好了。
类CommonWindow继承AResponsibilityChainCommonWindow,实现服务客户的方法。
package com.garychow.classes;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.garychow.interfaces.ICommonCustom;
public class CommonWindow extends AResponsibilityChainCommonWindow {
public CommonWindow(String name) {
this.name = name;
}
@Override
public boolean service(ICommonCustom custom) {
if (isServicing) {
return super.service(custom);
}
isServicing = true;
final ICommonCustom fCustom = custom;
executor.execute(new Runnable() {
@Override
public void run() {
try {
long beginTime = System.nanoTime();
System.out.println(name + "开始为普通顾客:" + fCustom.getName() + "服务");
Thread.sleep(fCustom.getNeedsTime());
long endTime = System.nanoTime();
System.out.println(name + "服务顾客" + fCustom.getName() + "完毕,服务时间为:" + (endTime - beginTime));
} catch (InterruptedException e) {
} finally {
isServicing = false;
WindowObserver.getInstance().commonWindowServiceFinish();
}
}
});
return true;
}
private Executor executor = Executors.newSingleThreadExecutor();
private boolean isServicing;
private String name;
}
VIPServiceWindow也继承AResponsibilityChainCommonWindow类,因为它也是处理普通客户的链上的一环,同时要实现IVIPServiceWindow接口,处理VIP客户服务。
package com.garychow.classes;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.garychow.interfaces.ICommonCustom;
import com.garychow.interfaces.IVIPCustom;
import com.garychow.interfaces.IVIPServiceWindow;
public class VIPServiceWindow extends AResponsibilityChainCommonWindow implements IVIPServiceWindow {
public VIPServiceWindow(String name) {
this.name = name;
}
@Override
public boolean service(ICommonCustom custom) {
if (isServicing) {
return super.service(custom);
}
isServicing = true;
final ICommonCustom fCustom = custom;
executor.execute(new Runnable() {
@Override
public void run() {
try {
long beginTime = System.nanoTime();
System.out.println(name + "开始为普通顾客:" + fCustom.getName() + "服务");
Thread.sleep(fCustom.getNeedsTime());
long endTime = System.nanoTime();
System.out.println(name + "服务顾客" + fCustom.getName() + "完毕,服务时间为:" + (endTime - beginTime));
} catch (InterruptedException e) {
} finally {
isServicing = false;
WindowObserver.getInstance().commonWindowServiceFinish();
}
}
});
return true;
}
@Override
public boolean service(IVIPCustom custom) {
if (isServicing) {
return false;
}
isServicing = true;
final IVIPCustom fCustom = custom;
executor.execute(new Runnable() {
@Override
public void run() {
try {
long beginTime = System.nanoTime();
System.out.println(name + "开始为VIP顾客:" + fCustom.getName() + "服务");
Thread.sleep(fCustom.getNeedsTime());
long endTime = System.nanoTime();
System.out.println(name + "服务顾客" + fCustom.getName() + "完毕,服务时间为:" + (endTime - beginTime));
} catch (InterruptedException e) {
} finally {
isServicing = false;
WindowObserver.getInstance().vipWindowServiceFinish();
}
}
});
return true;
}
private Executor executor = Executors.newSingleThreadExecutor();
private boolean isServicing;
private String name;
}
同样的,ExpressServiceWindow和VIPServiceWdinow类似。
package com.garychow.classes;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.garychow.interfaces.ICommonCustom;
import com.garychow.interfaces.IExpressCustom;
import com.garychow.interfaces.IExpressServiceWindow;
public class ExpressServiceWindow extends AResponsibilityChainCommonWindow
implements IExpressServiceWindow {
public ExpressServiceWindow(String name) {
this.name = name;
}
@Override
public boolean service(IExpressCustom custom) {
if (isServicing) {
return false;
}
isServicing = true;
final IExpressCustom fCustom = custom;
executor.execute(new Runnable() {
@Override
public void run() {
try {
long beginTime = System.nanoTime();
System.out.println(name + "开始为快速顾客:" + fCustom.getName() + "服务");
Thread.sleep(fCustom.getNeedsTime());
long endTime = System.nanoTime();
System.out.println(name + "服务顾客" + fCustom.getName() + "完毕,服务时间为:" + (endTime - beginTime));
} catch (InterruptedException e) {
} finally {
isServicing = false;
WindowObserver.getInstance().expressWindowServiceFinish();
}
}
});
return true;
}
@Override
public boolean service(ICommonCustom custom) {
if (isServicing) {
return super.service(custom);
}
isServicing = true;
final ICommonCustom fCustom = custom;
executor.execute(new Runnable() {
@Override
public void run() {
try {
long beginTime = System.nanoTime();
System.out.println(name + "开始为普通顾客:" + fCustom.getName() + "服务");
Thread.sleep(fCustom.getNeedsTime());
long endTime = System.nanoTime();
System.out.println(name + "服务顾客" + fCustom.getName() + "完毕,服务时间为:" + (endTime - beginTime));
} catch (InterruptedException e) {
} finally {
isServicing = false;
WindowObserver.getInstance().commonWindowServiceFinish();
}
}
});
return true;
}
private Executor executor = Executors.newSingleThreadExecutor();
private boolean isServicing;
private String name;
}
写到这里,只需要把调度系统给实现就好了。代码如下:
package com.garychow.classes;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import com.garychow.interfaces.ICommonCustom;
import com.garychow.interfaces.ICommonServiceWindow;
import com.garychow.interfaces.IExpressCustom;
import com.garychow.interfaces.IExpressServiceWindow;
import com.garychow.interfaces.INumberMachine;
import com.garychow.interfaces.IVIPCustom;
import com.garychow.interfaces.IVIPServiceWindow;
public class NumberMachine implements INumberMachine {
private static class NumberMachineHolder {
public final static NumberMachine nm = new NumberMachine();
}
private NumberMachine() {
executor = Executors.newSingleThreadExecutor();
commonCustoms = new LinkedBlockingQueue<ICommonCustom>();
vipCustoms = new LinkedBlockingQueue<IVIPCustom>();
expressCustoms = new LinkedBlockingQueue<IExpressCustom>();
AResponsibilityChainCommonWindow cw1 = new CommonWindow("一号普通窗口");
AResponsibilityChainCommonWindow cw2 = new CommonWindow("二号普通窗口");
AResponsibilityChainCommonWindow cw3 = new CommonWindow("三号普通窗口");
AResponsibilityChainCommonWindow cw4 = new CommonWindow("四号普通窗口");
VIPServiceWindow vw = new VIPServiceWindow("VIP窗口");
ExpressServiceWindow ew = new ExpressServiceWindow("快速窗口");
cw1.setNextCommonServiceWindow(cw2);
cw2.setNextCommonServiceWindow(cw3);
cw3.setNextCommonServiceWindow(cw4);
cw4.setNextCommonServiceWindow(ew);
ew.setNextCommonServiceWindow(vw);
commonServiceWindowChainHeader = cw1;
expressServiceWindow = ew;
vipServiceWindow = vw;
}
public static INumberMachine getInstance() {
return NumberMachineHolder.nm;
}
@Override
public void generateCommonCustom() {
ICommonCustom custom = new CommonCustom();
commonCustoms.offer(custom);
serviceNextCommonCustom();
}
@Override
public void generateVIPCustom() {
IVIPCustom custom = new VIPCustom();
vipCustoms.offer(custom);
serviceNextVIPCustom();
}
@Override
public void generateExpressCustom() {
IExpressCustom custom = new ExpressCustom();
expressCustoms.offer(custom);
serviceNextExpressCustom();
}
@Override
public void serviceNextCommonCustom() {
executor.execute(new Runnable() {
@Override
public void run() {
serviceNextCommonCustom(commonServiceWindowChainHeader);
}
});
}
@Override
public void serviceNextVIPCustom() {
executor.execute(new Runnable() {
@Override
public void run() {
if (!serviceNextVIPCustom(vipServiceWindow)) {
serviceNextCommonCustom(vipServiceWindow);
}
}
});
}
@Override
public void serviceNextExpressCustom() {
executor.execute(new Runnable() {
@Override
public void run() {
if (!serviceNextExpressCustom(expressServiceWindow)) {
serviceNextCommonCustom(expressServiceWindow);
}
}
});
}
private boolean serviceNextCommonCustom(ICommonServiceWindow window) {
boolean isServiced = false;
if (!commonCustoms.isEmpty()) {
ICommonCustom custom = commonCustoms.element();
if (isServiced = window.service(custom)) {
commonCustoms.poll();
}
}
return isServiced;
}
private boolean serviceNextExpressCustom(IExpressServiceWindow window) {
boolean isServiced = false;
if (!expressCustoms.isEmpty()) {
IExpressCustom custom = expressCustoms.element();
if (isServiced = window.service(custom)) {
expressCustoms.poll();
}
}
return isServiced;
}
private boolean serviceNextVIPCustom(IVIPServiceWindow window) {
boolean isServiced = false;
if (!vipCustoms.isEmpty()) {
IVIPCustom custom = vipCustoms.element();
if (isServiced = window.service(custom)) {
vipCustoms.poll();
}
}
return isServiced;
}
private final Executor executor;
private final Queue<ICommonCustom> commonCustoms;
private final Queue<IVIPCustom> vipCustoms;
private final Queue<IExpressCustom> expressCustoms;
private final AResponsibilityChainCommonWindow commonServiceWindowChainHeader;
private final ExpressServiceWindow expressServiceWindow;
private final VIPServiceWindow vipServiceWindow;
}
最后,写出场景类调用就可以模拟银行调度系统了。
package com.garychow.classes;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.garychow.interfaces.INumberMachine;
public class Context {
public static void main(String[] args) {
final INumberMachine nm = NumberMachine.getInstance();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
double random = Math.random();
if (random < 0.1) {
nm.generateVIPCustom();
} else if (random >= 0.1 && random < 0.4) {
nm.generateExpressCustom();
} else {
nm.generateCommonCustom();
}
}
}, 0, Constants.GENERATE_INTERVAL_TIME, TimeUnit.SECONDS);
}
}
到此,这个题目就算完全做完了。