黑马程序员-7K月薪面试题破解之二_银行业务调度系统

本文介绍了一个银行窗口调度系统的实现方案,通过面向对象的设计方法解决了不同类型客户的服务调度问题。系统包括号码管理器、号码机器和窗口服务类等核心组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

面向对象的分析与设计

(1)      有三种对应类型的客户:VIP客户,普通客户,快速客户,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务

(2)      各类型客户在其对应窗口按顺序依次办理业务,准确地说,应该是窗口依次叫号

 

NumberManager

定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。

这里要注意的是多线程并发的安全问题

generateNewManager()fetchServiceNumber()

都是在操作同一个数据lastNumber一个在取,一个在添加,两者不能同时进行

所以必须给这两个方法加上锁标记synchronied

代码:

package com.isoftstone.interview.bank;

import java.util.ArrayList;
import java.util.List;

public class NumberManager {
	private int lastNumber = 0;
	//产生第一个号码
	private List<Integer> queueNumber = new ArrayList<Integer>();
	//创建一个集合,装排队的号码	
	public synchronized Integer generateNewManager()
	{//产生号码
		queueNumber.add(++lastNumber);
		return lastNumber;
	}
	
	public synchronized Integer fetchServiceNumber()
	{//取出一个号码
		Integer number = null;
		if(queueNumber.size()>0){
			number = queueNumber.remove(0);
		}
		return number;
	}
}


 

NumberMachine

定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。

NumberMachine类设计成单例

代码:

package com.isoftstone.interview.bank;

public class NumberMachine {
	private NumberManager commonManager = new NumberManager();
	private NumberManager expressManager = new NumberManager();
	private NumberManager VIPManager = new NumberManager();
	
	private void NumberMachine() {
		// 把构造函数私有化,利用单例模式
	}
	public static NumberMachine getInstance() {
		// 返回一个对象
		return instance;
	}

	private static NumberMachine instance = new NumberMachine();
	// 要定义为静态

	public NumberManager getCommonManager() {
		return commonManager;
	}

	public NumberManager getExpressManager() {
		return expressManager;
	}

	public NumberManager getVIPManager() {
		return VIPManager;
	}

}


CustomerType枚举类

系统中有三种类型的客户,所以用定义一个枚举类,其中定义三个成员分别表示三种类型的客户。

重写toString方法,返回类型的中文名称。这是在后面编码时重构出来的,刚开始不用考虑。

代码:

package com.isoftstone.interview.bank;

public enum CustomerType {
	COMMON,EXPRESS,VIP;
	public String toString(){
		String name = null;
		switch(this){
		case COMMON:
			name = "普通";
			break;
		case EXPRESS:
			name = "快速";
			break;
		case VIP:
			name = "VIP";
			break;
		}
		return name;
	}
}


ServiceWindow

定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法。

定义三个方法分别对三种客户进行服务,为了观察运行效果,应详细打印出其中的细节信息。

代码:

package com.isoftstone.interview.bank;

import java.util.Random;
import java.util.concurrent.Executors;

public class ServiceWindow {
	private CustomerType type = CustomerType.COMMON;
	private int windowId = 1;
	
	public void setType(CustomerType type) {
		this.type = type;
	}

	public void setWindowId(int windowId) {
		this.windowId = windowId;
	}
	
	public void start(){
		Executors.newSingleThreadExecutor().execute(new Runnable(){
			public void run(){
				while(true){
					switch(type){
					case COMMON:
						commonService();
						break;
					case EXPRESS:
						expressService();
						break;
					case VIP:
						vipService();
						break;
					}
					//NumberMachine.getInstance().getCommonManger();
				}
			}
		});
	}
	private void commonService() {
		String windowName = "第" + windowId +"号"+type+"窗口";
		System.out.println(windowName+"正在叫号");
		//得到号码机器,并取出普通号码管理器,并取出一个号码
		Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber();
		//如果返回的号码不为空
		if(number!=null){
			System.out.println(windowName + "开始为第" + number + "号普通客户服务");	
			//记录当前的系统时间
			long beginTime = System.currentTimeMillis();
			//确定处理业务的耗时范围
			int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
			//得到一个在范围内的一个实际随机耗时
			long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
			try {//让程序睡眠指定的时间,模拟业务耗时
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//得到现在的系统时间,并减去之前的系统时间,得到模拟处理业务的耗时
			long costTime = System.currentTimeMillis()-beginTime;
			System.out.println(windowName + "为第" + number+"个"+type+"客户完成服务,耗时" +costTime/1000 + "秒");
		}else{
			System.out.println(windowName+"没有取到服务任务");
		}
	}
	
	private void expressService() {
		String windowName = "第" + windowId +"号"+type+"窗口";
		System.out.println(windowName+"正在叫号");
		//得到号码机器,并取出普通号码管理器,并取出一个号码
		Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
		//如果返回的号码不为空
		if(number!=null){
			//记录当前的系统时间
			System.out.println(windowName + "开始为第" + number + "号快速客户服务");	
			long beginTime = System.currentTimeMillis();
			//确定处理业务的耗时范围
			int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
			//得到一个在范围内的一个实际随机耗时
			long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
			try {//让程序睡眠指定的时间,模拟业务耗时
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//得到现在的系统时间,并减去之前的系统时间,得到模拟处理业务的耗时
			long costTime = System.currentTimeMillis()-beginTime;
			System.out.println(windowName + "为第" + number+"个"+type+"客户完成服务,耗时" +costTime/1000 + "秒");
		}else{
			System.out.println(windowName+"没有取到服务任务");
			commonService();
			try {
				Thread.sleep(Constants.MIN_SERVICE_TIME);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void vipService() {
		String windowName = "第" + windowId +"号"+type+"窗口";
		System.out.println(windowName+"正在叫号");
		//得到号码机器,并取出普通号码管理器,并取出一个号码
		Integer number = NumberMachine.getInstance().getVIPManager().fetchServiceNumber();
		//如果返回的号码不为空
		if(number!=null){
			System.out.println(windowName + "开始为第" + number + "号VIP客户服务");	
			//记录当前的系统时间
			long beginTime = System.currentTimeMillis();
			//确定处理业务的耗时范围
			int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
			//得到一个在范围内的一个实际随机耗时
			long serviceTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
			try {//让程序睡眠指定的时间,模拟业务耗时
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//得到现在的系统时间,并减去之前的系统时间,得到模拟处理业务的耗时
			long costTime = System.currentTimeMillis()-beginTime;
			System.out.println(windowName + "为第" + number+"个"+type+"客户完成服务,耗时" +costTime/1000 + "秒");
		}else{
			System.out.println(windowName+"没有取到服务任务");
			commonService();
			try {
				Thread.sleep(Constants.MIN_SERVICE_TIME);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}	
}


MainClass

for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。

接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。

代码:

package com.isoftstone.interview.bank;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MainClass {
	public static void main(String[] args) throws InterruptedException{
		for(int i=0;i<5;i++){
			ServiceWindow commonWindow = new ServiceWindow();
			commonWindow.setWindowId(i);
			commonWindow.start();
		}
		
		ServiceWindow expressWindow = new ServiceWindow();
		expressWindow.setType(CustomerType.EXPRESS);
		expressWindow.start();
		
		ServiceWindow vipWindow = new ServiceWindow();
		vipWindow.setType(CustomerType.VIP);
		vipWindow.start();
		
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer number = NumberMachine.getInstance().getCommonManager().generateNewManager();
						System.out.println(number+"号普通客户在等待服务");
					}
				}, 
				0, 
				Constants.COMMON_CUST_TIME, 
				TimeUnit.MILLISECONDS );
		
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager();
						System.out.println(number+"号快速客户在等待服务");
					}
				}, 
				0, 
				Constants.COMMON_CUST_TIME*3, 
				TimeUnit.MILLISECONDS );
		
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer number = NumberMachine.getInstance().getVIPManager().generateNewManager();
						System.out.println(number+"号VIP客户在等待服务");
					}
				}, 
				0, 
				Constants.COMMON_CUST_TIME*6, 
				TimeUnit.MILLISECONDS );
	}
}


Constants

定义三个常量:

MAX_SERVICE_TIMEMIN_SERVICE_TIMECOMMON_CUSTOMER_INTERVAL_TIME

代码:

package com.isoftstone.interview.bank;

public class Constants {
	public static int MAX_SERVICE_TIME = 1000;
	public static int MIN_SERVICE_TIME = 100;
	public static int COMMON_CUST_TIME = 100;
}

总结:

银行业务调度这个项目,关键的点在于面向对象的分析,联系生活

(1)      首先确定存在哪几个对象

此项目中存在三个类1:产生号码的管理器 2:选择窗口的号码机器 3:窗口

(2)      确定类与类之间有什么关系

选择窗口的机器只有一台,使用单例模式创建对象,不同的窗口有各自对应的选择窗口的机器,所以要创建三个对象,选择窗口的机器对象操作产生号码的管理器,窗口调用号码管理器产生的号码

(3)      对象中的大概具有什么功能

产生号码的管理器类必须提供产生新号码以及删除号码的方法

选择窗口的号码机器能根据不同的用户调用不同号码产生器的方法


 

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.youkuaiyun.com 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值